introduce OptionString, (#1352)
authortsteven4 <13596209+tsteven4@users.noreply.github.com>
Wed, 30 Oct 2024 00:24:17 +0000 (18:24 -0600)
committerGitHub <noreply@github.com>
Wed, 30 Oct 2024 00:24:17 +0000 (18:24 -0600)
* introduce OptionString,

a replacement for OptionCString. Formats and filters can be
manually converted, with the potential to eliminate c character
string usage.
OptionCString::get() usages can often use an implicit cast from
OptionString instead, although the usage of get() is still valid.

* more OptionCString -> OptionString conversion

* use custom conversion routines.

this introduces OptionString::toInt and OptionString::toDouble,
which enforce error checking on conversions.

* convert more formats to OptionString

* convert gdb to OptionString

* convert xcsv, unicsv to OptionString

* convert html, text to OptionString

* convert ozi to OptionString

* convert mtk to OptionString

* convert globalsat to OptionString

* convert exif to OptionString

* convert lowranceusr to OptionString

* convert igc to OptionString.

note the error checking on timeadj is now done by OptionString::toInt.

* prove igc timeadj works with ints.

* convert shape to OptionString

* convert garmin_gpi to OptionString

* consolidate integer/double parse routines.

* convert subrip to OptionString

* convert garmin_txt to OptionString

* enhance OptionString conversions.

overloads, as opposed to default parameters, allow tools to find differen usages.

improved OptionString error messages with module and argstring information.

* enhance argtype ...

to indicate integer base and if trailing data is allowed.
Use these fields to check and convert integer/doubles in Vecs, fataling on errors.

* allow trailng data with parse_distance, parse_speed.

and when necessary convert these from ARGTYPE_STRING to ARGTYPE_FLOAT.

* fix typo, update refs

* convert garmin to OptionString

* convert skytraq to OptionString

* Introduce OptionInt & OptionDouble.

These must be used with ARGTYPE_INT and ARGTYPE_FLOAT.
The data is checked and converted in Vecs, and the results may
be retrieved by the user.

* convert almost all ARGTYPE_INT to OptionInt.

* final conversion to OptionInt

* mark TODO done.

* correct include comments for greps sake.

* delete complete TODO comments.

* whitespace

* use consistent error messags regarding options.

i.e. "module(option): "

* delete unused Option method type

* use ARGTYPE_STRING if trailing data is allowed.

The GUI validator used with ARGTYPE_INT and ARGTYPE_FLOAT will
reject input with any trailing data.

The CLI, including Vecs::assign_option, keys off the Option subclass now,
not the ARGTYPE.

Note that OptionInt and OptionDouble still allow trailing data.  If
trailing data is allowed with these classes we just pair these with
ARGTYPE_STRING which controls validation in the GUI.

* drive arg validation from Option classes.

* simplify use of isEmpty

delete unused macro
delete extraneous file

* non member operator== with parameters const QString& not a candidate,

parameters are of class OptionString.

87 files changed:
CMakeLists.txt
arcdist.cc
arcdist.h
bend.cc
bend.h
defs.h
discard.cc
discard.h
exif.cc
exif.h
garmin.cc
garmin.h
garmin_gpi.cc
garmin_gpi.h
garmin_txt.cc
garmin_txt.h
garmin_xt.cc
garmin_xt.h
gdb.cc
gdb.h
geo.cc
geo.h
globalsat_sport.cc
globalsat_sport.h
gpx.cc
gpx.h
gtrnctr.cc
gtrnctr.h
height.cc
height.h
html.cc
html.h
igc.cc
igc.h
interpolate.cc
interpolate.h
kml.cc
kml.h
lowranceusr.cc
lowranceusr.h
mtk_logger.cc
mtk_logger.h
nmea.cc
nmea.h
option.cc [new file with mode: 0644]
option.h
osm.cc
osm.h
ozi.cc
ozi.h
parse.cc
polygon.cc
polygon.h
position.cc
position.h
radius.cc
radius.h
random.cc
random.h
reference/filter1.txt
reference/format3.txt
resample.cc
resample.h
shape.h
skytraq.cc
skytraq.h
smplrout.cc
smplrout.h
stackfilter.cc
stackfilter.h
subrip.cc
subrip.h
testo.d/igc.test
text.cc
text.h
tpg.cc
tpg.h
trackfilter.cc
trackfilter.h
transform.cc
transform.h
unicsv.cc
unicsv.h
vecs.cc
vecs.h
xcsv.cc
xcsv.h

index c0841ee75fb233bce45d8a242c58030d0f54fe76..b8a0a9b67d33bd723545d0a1dd6df77b690d1b43 100644 (file)
@@ -168,6 +168,7 @@ set(SUPPORT
   inifile.cc
   main.cc
   mkshort.cc
+  option.cc
   parse.cc
   rgbcolors.cc
   route.cc
index c72ee1a8975a89d2130f746074635836f44c8d2a..cd0349a2a2f0ce60701a9914119024ad34c1b29e 100644 (file)
@@ -112,7 +112,7 @@ void ArcDistanceFilter::process()
     QString line;
 
     gpsbabel::TextStream stream;
-    stream.open(arcfileopt.get(), QIODevice::ReadOnly, MYNAME);
+    stream.open(arcfileopt, QIODevice::ReadOnly, MYNAME);
 
     auto* arcpt1 = new Waypoint;
     auto* arcpt2 = new Waypoint;
index 47526788deb455d2d8fce89d9886ed4232fcbbba..717b6efcae827d8f50a7ce61d7c4473f46d45c8e 100644 (file)
--- a/arcdist.h
+++ b/arcdist.h
@@ -28,7 +28,7 @@
 
 #include "defs.h"    // for ARG_NOMINMAX, ARGTYPE_BOOL, Waypoint (ptr only)
 #include "filter.h"  // for Filter
-#include "option.h"  // for OptionBool, OptionCString
+#include "option.h"  // for OptionBool, OptionString
 
 #if FILTERS_ENABLED
 
@@ -61,8 +61,8 @@ private:
   /* Data Members */
 
   double pos_dist{};
-  OptionCString distopt;
-  OptionCString arcfileopt;
+  OptionDouble distopt;
+  OptionString arcfileopt;
   OptionBool rteopt;
   OptionBool trkopt;
   OptionBool exclopt;
@@ -84,7 +84,7 @@ private:
     },
     {
       "distance", &distopt, "Maximum distance from arc",
-      nullptr, ARGTYPE_FLOAT | ARGTYPE_REQUIRED, ARG_NOMINMAX, nullptr
+      nullptr,  ARGTYPE_ALLOW_TRAILING_DATA | ARGTYPE_STRING | ARGTYPE_REQUIRED, ARG_NOMINMAX, nullptr
     },
     {
       "exclude", &exclopt, "Exclude points close to the arc", nullptr,
diff --git a/bend.cc b/bend.cc
index 09325673f663ad797443f7ae0b383a633a8a63af..4f7abc2cba2c933581e9ce5853b189b93097fa2b 100644 (file)
--- a/bend.cc
+++ b/bend.cc
@@ -21,7 +21,7 @@
  */
 
 #include <cmath>            // macos wants abs from here!
-#include <cstdlib>          // for strtod, abs
+#include <cstdlib>          // for abs
 #include <utility>          // for as_const
 
 #include <QString>          // for QString
@@ -41,12 +41,12 @@ void BendFilter::init()
 {
   maxDist = 0.0;
   if (distopt) {
-    maxDist = strtod(distopt, nullptr);
+    maxDist = distopt.get_result();
   }
 
   minAngle = 0.0;
   if (minangleopt) {
-    minAngle = strtod(minangleopt, nullptr);
+    minAngle = minangleopt.get_result();
   }
 
   route_backup(&routes_orig);
diff --git a/bend.h b/bend.h
index 73cf803e80bdf5f794489ea67adf416cf8804ab7..fd7b101d5c325bd4381f559baff404e23338830c 100644 (file)
--- a/bend.h
+++ b/bend.h
@@ -29,7 +29,7 @@
 
 #include "defs.h"    // for route_head (ptr only), ARGTYPE_FLOAT, ARG_NOMINMAX
 #include "filter.h"  // for Filter
-#include "option.h"  // for OptionCString
+#include "option.h"  // for OptionString
 
 #if FILTERS_ENABLED
 
@@ -45,8 +45,8 @@ public:
   void deinit() override;
 
 private:
-  OptionCString distopt;
-  OptionCString minangleopt;
+  OptionDouble distopt;
+  OptionDouble minangleopt;
 
   double maxDist{};
   double minAngle{};
diff --git a/defs.h b/defs.h
index ac8c54382fc802d1c3d4136bb2090f7948f792f8..34baa610fe501b66b1ae36208e29dd401adbc16e 100644 (file)
--- a/defs.h
+++ b/defs.h
@@ -866,8 +866,18 @@ extern posn_status tracking_status;
  * for "groups" of exactly one option. */
 #define ARGTYPE_BEGIN_REQ  0x04000000U
 #define ARGTYPE_END_REQ    0x02000000U
+/* For integer conversions specify the base to allow strict error checking and
+ * proper conversion in Vecs */
+#define ARGTYPE_BASE_10   0x00000000U
+#define ARGTYPE_BASE_AUTO 0x00100000U
+#define ARGTYPE_BASE_16   0x00200000U
+
+/* For integer and double conversions is trailing data allowed?
+ * This allows strict error checking in Vecs */
+#define ARGTYPE_ALLOW_TRAILING_DATA 0x00400000U
 
 #define ARGTYPE_TYPEMASK 0x00000fffU
+#define ARGTYPE_BASEMASK 0x00300000U
 #define ARGTYPE_FLAGMASK 0xfffff000U
 
 #define ARG_NOMINMAX nullptr, nullptr
@@ -1025,13 +1035,13 @@ int xstrtoi(const char* str, char** str_end, int base);
 /*
  *  From parse.c
  */
+int parse_integer(const QString& str, const QString& id, bool* ok = nullptr, QString* end = nullptr, int base = 10);
+double parse_double(const QString& str, const QString& id, bool* ok = nullptr, QString* end = nullptr);
 int parse_coordinates(const char* str, int datum, grid_type grid,
                       double* latitude, double* longitude, const char* module);
 int parse_coordinates(const QString& str, int datum, grid_type grid,
                       double* latitude, double* longitude, const char* module);
-int parse_distance(const char* str, double* val, double scale, const char* module);
 int parse_distance(const QString& str, double* val, double scale, const char* module);
-int parse_speed(const char* str, double* val, double scale, const char* module);
 int parse_speed(const QString& str, double* val, double scale, const char* module);
 
 /*
index 34ea92258ca335bd1c194463351c5204ca918a58..39a7960e65f388bbb719ca8d7e7e39ac934ca7aa 100644 (file)
@@ -24,9 +24,7 @@
 #include <QDebug>              // for QDebug
 #include <QRegularExpression>  // for QRegularExpression, QRegularExpression::CaseInsensitiveOption, QRegularExpressionMatch
 
-#include <cstdlib>             // for strtod
-
-#include "defs.h"              // for Waypoint, fatal, route_head (ptr only), xstrtoi, del_marked_wpts, route_del_marked_wpts, route_disp_all, track_del_marked_wpts, track_disp_all, waypt_disp_all, fix_none, fix_unknown
+#include "defs.h"              // for Waypoint, fatal, route_head (ptr only), del_marked_wpts, route_del_marked_wpts, route_disp_all, track_del_marked_wpts, track_disp_all, waypt_disp_all, fix_none, fix_unknown
 #include "src/core/logging.h"  // for FatalMsg
 
 
@@ -131,51 +129,51 @@ QRegularExpression DiscardFilter::generateRegExp(const QString& glob_pattern)
 void DiscardFilter::init()
 {
   if (hdopopt) {
-    hdopf = strtod(hdopopt, nullptr);
+    hdopf = hdopopt.get_result();
   } else {
     hdopf = -1.0;
   }
 
   if (vdopopt) {
-    vdopf = strtod(vdopopt, nullptr);
+    vdopf = vdopopt.get_result();
   } else {
     vdopf = -1.0;
   }
 
   if (satopt) {
-    satpf = xstrtoi(satopt, nullptr, 10);
+    satpf = satopt.get_result();
   } else {
     satpf = -1;
   }
 
   if (eleminopt) {
-    eleminpf = xstrtoi(eleminopt, nullptr, 10);
+    eleminpf = eleminopt.get_result();
   }
 
   if (elemaxopt) {
-    elemaxpf = xstrtoi(elemaxopt, nullptr, 10);
+    elemaxpf = elemaxopt.get_result();
   }
 
   if (nameopt) {
-    name_regex = generateRegExp(nameopt.get());
+    name_regex = generateRegExp(nameopt);
     if (!name_regex.isValid()) {
       fatal(FatalMsg() << "discard: matchname option is an invalid expression.");
     }
   }
   if (descopt) {
-    desc_regex = generateRegExp(descopt.get());
+    desc_regex = generateRegExp(descopt);
     if (!desc_regex.isValid()) {
       fatal(FatalMsg() << "discard: matchdesc option is an invalid expression.");
     }
   }
   if (cmtopt) {
-    cmt_regex = generateRegExp(cmtopt.get());
+    cmt_regex = generateRegExp(cmtopt);
     if (!cmt_regex.isValid()) {
       fatal(FatalMsg() << "discard: matchcmt option is an invalid expression.");
     }
   }
   if (iconopt) {
-    icon_regex = generateRegExp(iconopt.get());
+    icon_regex = generateRegExp(iconopt);
     if (!icon_regex.isValid()) {
       fatal(FatalMsg() << "discard: matchicon option is an invalid expression.");
     }
index 1325d26002a167dc0347e3eb1eddaa3b5be327b9..73c82bebc5d58657f4fd0bab0d06ff9e79f4c4a0 100644 (file)
--- a/discard.h
+++ b/discard.h
@@ -29,7 +29,7 @@
 
 #include "defs.h"              // for arglist_t, ARG_NOMINMAX, ARGTYPE_BEGIN_REQ, ARGTYPE_STRING, ARGTYPE_BOOL, ARGTYPE_INT, ARGTYPE_FLOAT, route_head, ARGTYPE_END_REQ, Waypoint, gpsdata_type
 #include "filter.h"            // for Filter
-#include "option.h"            // for OptionCString, OptionBool
+#include "option.h"            // for OptionString, OptionBool
 
 #if FILTERS_ENABLED
 class DiscardFilter:public Filter
@@ -50,21 +50,21 @@ private:
 
   /* Data Members */
 
-  OptionCString hdopopt;
-  OptionCString vdopopt;
+  OptionDouble hdopopt;
+  OptionDouble vdopopt;
   OptionBool andopt;
-  OptionCString satopt;
+  OptionInt satopt;
   OptionBool fixnoneopt;
   OptionBool fixunknownopt;
-  OptionCString eleminopt;
-  OptionCString elemaxopt;
-  OptionCString nameopt;
+  OptionInt eleminopt;
+  OptionInt elemaxopt;
+  OptionString nameopt;
   QRegularExpression name_regex;
-  OptionCString descopt;
+  OptionString descopt;
   QRegularExpression desc_regex;
-  OptionCString cmtopt;
+  OptionString cmtopt;
   QRegularExpression cmt_regex;
-  OptionCString iconopt;
+  OptionString iconopt;
   QRegularExpression icon_regex;
 
   double hdopf{};
@@ -88,7 +88,7 @@ private:
     },
     {
       "sat", &satopt, "Minimum sats to keep points",
-      "-1.0", ARGTYPE_BEGIN_REQ | ARGTYPE_INT, ARG_NOMINMAX, nullptr
+      "-1", ARGTYPE_BEGIN_REQ | ARGTYPE_INT, ARG_NOMINMAX, nullptr
     },
     {
       "fixnone", &fixnoneopt, "Suppress points without fix",
diff --git a/exif.cc b/exif.cc
index 4f6ec2c29532981320f59fe5460e858929483abc..e0bfbfec312f2ac8a542b0e738492ea0269e5323 100644 (file)
--- a/exif.cc
+++ b/exif.cc
@@ -722,7 +722,7 @@ ExifFormat::exif_get_exif_time(ExifApp* app) const
     }
 
     if (offset_tag || opt_offsettime) {
-      QByteArray time_tag = opt_offsettime? QByteArray(opt_offsettime) : exif_read_str(offset_tag);
+      QString time_tag = opt_offsettime? opt_offsettime : QString(exif_read_str(offset_tag));
       // string should be +HH:MM or -HH:MM
       static const QRegularExpression re(R"(^([+-])(\d{2}):(\d{2})$)");
       assert(re.isValid());
@@ -739,7 +739,7 @@ ExifFormat::exif_get_exif_time(ExifApp* app) const
       } else if (opt_offsettime) {
         // Only warn for user supplied offsets.
         // Offset tags may indicate the offset was unknown, e.g. "   :  ".
-        warning(MYNAME ": OffsetTime is expected to be +HH:MM or -HH:MM, but was %s.\n", time_tag.constData());
+        warning(MYNAME ": OffsetTime is expected to be +HH:MM or -HH:MM, but was %s.\n", qPrintable(time_tag));
       }
     }
 
@@ -1178,7 +1178,7 @@ ExifFormat::exif_find_wpt_by_name(const Waypoint* wpt)
 {
   if (exif_wpt_ref != nullptr) {
     return;
-  } else if ((wpt->shortname != nullptr) && (case_ignore_strcmp(wpt->shortname, opt_name.get()) == 0)) {
+  } else if ((wpt->shortname != nullptr) && (case_ignore_strcmp(wpt->shortname, opt_name) == 0)) {
     exif_wpt_ref = wpt;
   }
 }
@@ -1530,7 +1530,7 @@ ExifFormat::write()
       track_disp_all(nullptr, nullptr, exif_find_wpt_by_name_lambda);
     }
     if (exif_wpt_ref == nullptr) {
-      warning(MYNAME ": No matching point with name \"%s\" found.\n", qPrintable(opt_name.get()));
+      warning(MYNAME ": No matching point with name \"%s\" found.\n", qPrintable(opt_name));
     }
   } else {
     auto exif_find_wpt_by_time_lambda = [this](const Waypoint* waypointp)->void {
@@ -1540,7 +1540,7 @@ ExifFormat::write()
     route_disp_all(nullptr, nullptr, exif_find_wpt_by_time_lambda);
     waypt_disp_all(exif_find_wpt_by_time_lambda);
 
-    qint64 frame = xstrtoi(opt_frame, nullptr, 10);
+    qint64 frame = opt_frame.get_result();
 
     if (exif_wpt_ref == nullptr) {
       warning(MYNAME ": No point with a valid timestamp found.\n");
diff --git a/exif.h b/exif.h
index e6ac7409d7c092cc642cf405b97f517b7aea91ee..050acfb4cdca329c8e59b5797d796f0108f928ee 100644 (file)
--- a/exif.h
+++ b/exif.h
@@ -50,7 +50,7 @@
 #include "defs.h"      // for arglist_t, ff_cap, Waypoint, ARG_NOMINMAX, ARGTYPE_BOOL, ff_cap_none, ARGTYPE_INT, ARGTYPE_STRING, ff_cap_read, ff_cap_write, ff_type, ff_type_file
 #include "format.h"    // for Format
 #include "gbfile.h"    // for gbfile, gbsize_t
-#include "option.h"    // for OptionCString, OptionBool
+#include "option.h"    // for OptionString, OptionBool
 
 
 class ExifFormat : public Format
@@ -205,9 +205,9 @@ private:
 
   OptionBool opt_filename;
   OptionBool opt_overwrite;
-  OptionCString opt_frame;
-  OptionCString opt_name;
-  OptionCString opt_offsettime;
+  OptionInt opt_frame;
+  OptionString opt_name;
+  OptionString opt_offsettime;
 
   QVector<arglist_t> exif_args = {
     { "filename", &opt_filename, "Set waypoint name to source filename", "Y", ARGTYPE_BOOL, ARG_NOMINMAX, nullptr },
index fffc175ca408163108e03e61676c8409324f439f..1f686f67acaa6f482c014d162c4b453ee0a2ddac 100644 (file)
--- a/garmin.cc
+++ b/garmin.cc
 #include <cmath>                 // for atan2, modf, sqrt
 #include <cstdio>                // for fprintf, fflush, snprintf, snprintf
 #include <cstdint>               // for int32_t
-#include <cstdlib>               // for strtol
 #include <cstring>               // for memcpy, strlen, strncpy, strchr
 #include <ctime>                 // for time_t
 #include <utility>               // for as_const
 
 #include <QByteArray>            // for QByteArray
 #include <QChar>                 // for QChar
-#include <QList>                 // for QList<>::const_iterator
 #include <QString>               // for QString
 #include <QTextCodec>            // for QTextCodec
 #include <Qt>                    // for CaseInsensitive
@@ -80,7 +78,7 @@ void
 GarminFormat::rw_init(const QString& fname)
 {
   receiver_must_upper = true;
-  const char* receiver_charset = "US-ASCII";
+  QByteArray receiver_charset = "US-ASCII";
   /* Technically, even this is a little loose as spaces aren't allowed */
   const char* valid_waypt_chars = MILITANT_VALID_WAYPT_CHARS " ";
 
@@ -102,12 +100,11 @@ GarminFormat::rw_init(const QString& fname)
     return;
   }
 
-  if (categorybitsopt) {
-    categorybits = strtol(categorybitsopt, nullptr, 0);
-  }
+  category = categoryopt? (1 << (categoryopt.get_result() - 1)) : 0;
+  categorybits = categorybitsopt? categorybitsopt.get_result() : 0;
 
   if (baudopt) {
-    baud = strtol(baudopt, nullptr, 0);
+    baud = baudopt.get_result();
     switch (baud) {
     case 9600:
     case 19200:
@@ -258,7 +255,7 @@ GarminFormat::rw_init(const QString& fname)
    * If the user provided a short_length, override the calculated value.
    */
   if (snlen) {
-    mkshort_handle->set_length(xstrtoi(snlen, nullptr, 10));
+    mkshort_handle->set_length(snlen.get_result());
   } else {
     mkshort_handle->set_length(receiver_short_length);
   }
@@ -287,13 +284,13 @@ GarminFormat::rw_init(const QString& fname)
    * However, this is still used for garmin_fs_garmin_after_read,
    * garmin_fs_garmin_before_write.
    */
-  if (opt_codec != nullptr) {
+  if (opt_codec) {
     // override expected codec with user supplied choice.
-    receiver_charset = opt_codec;
+    receiver_charset = opt_codec.get().toUtf8();
   }
   codec = get_codec(receiver_charset);
   if (global_opts.verbose_status) {
-    fprintf(stdout, "receiver charset detected as %s.\r\n", receiver_charset);
+    fprintf(stdout, "receiver charset detected as %s.\r\n", receiver_charset.constData());
   }
 
   valid_chars = valid_waypt_chars;
@@ -849,7 +846,7 @@ GarminFormat::waypoint_prepare()
     tx_waylist[i]->lat = wpt->latitude;
 
     if (deficon) {
-      icon = gt_find_icon_number_from_desc(deficon.get(), PCX);
+      icon = gt_find_icon_number_from_desc(deficon, PCX);
     } else {
       if (!wpt->gc_data->get_icon().isEmpty()) {
         icon = gt_find_icon_number_from_desc(wpt->gc_data->get_icon(), PCX);
@@ -877,7 +874,7 @@ GarminFormat::waypoint_prepare()
       tx_waylist[i]->time_populated = 1;
     }
     if (category) {
-      tx_waylist[i]->category = 1 << (xstrtoi(category, nullptr, 10) - 1);
+      tx_waylist[i]->category = category;
     }
     if (categorybits) {
       tx_waylist[i]->category = categorybits;
index a4f27aef5ce15178e74e538254007678deb5c86e..0be8a5ff15f80c8e7181f7d6a89584bc3c7f9a37 100644 (file)
--- a/garmin.h
+++ b/garmin.h
@@ -34,7 +34,7 @@
 #include "jeeps/gpsdevice.h"  // for gpsdevh
 #include "jeeps/gpssend.h"    // for GPS_PWay, GPS_SWay, GPS_PTrack, GPS_PPvt_Data, GPS_SLap
 #include "mkshort.h"          // for MakeShort
-#include "option.h"           // for OptionCString, OptionBool
+#include "option.h"           // for OptionString, OptionBool
 
 
 class GarminFormat : public Format
@@ -124,14 +124,15 @@ private:
   OptionBool poweroff;
   OptionBool eraset;
   OptionBool resettime;
-  OptionCString snlen;
+  OptionInt snlen;
   OptionBool snwhiteopt;
-  OptionCString deficon;
-  OptionCString category;
-  OptionCString categorybitsopt;
-  OptionCString baudopt;
-  OptionCString opt_codec;
+  OptionString deficon;
+  OptionInt categoryopt;
+  OptionInt categorybitsopt;
+  OptionInt baudopt;
+  OptionString opt_codec;
   int baud = 0;
+  int category{};
   int categorybits{};
   bool receiver_must_upper = true;
   QTextCodec* codec{nullptr};
@@ -165,12 +166,12 @@ private:
       nullptr, ARGTYPE_BOOL, ARG_NOMINMAX, nullptr
     },
     {
-      "category", &category, "Category number to use for written waypoints",
+      "category", &categoryopt, "Category number to use for written waypoints",
       nullptr, ARGTYPE_INT, "1", "16", nullptr
     },
     {
       "bitscategory", &categorybitsopt, "Bitmap of categories",
-      nullptr, ARGTYPE_INT, "1", "65535", nullptr
+      nullptr, ARGTYPE_BASE_AUTO | ARGTYPE_INT, "1", "65535", nullptr
     },
     {
       "baud", &baudopt, "Speed in bits per second of serial port (baud=9600)",
index 8cfd2365357995f961c456725f2cba46d1fb9cfa..00b79e3046c53820f6c83df6fef908c72c1661f7 100644 (file)
@@ -126,9 +126,9 @@ GarminGPIFormat::gpi_read_string(const char* field) const
         if (res1.strlen + 4 + res2.strlen + 4 != l0) {
           fatal(MYNAME ": Error out of sync (wrong size %d/%d/%d) on field '%s'!", l0, res1.strlen, res2.strlen, field);
         }
-        if (opt_lang && (opt_lang  == res1.lc)) {
+        if (opt_lang && (opt_lang.get().toUtf8()  == res1.lc)) {
           string = res1.str;
-        } else if (opt_lang && (opt_lang == res2.lc)) {
+        } else if (opt_lang && (opt_lang.get().toUtf8() == res2.lc)) {
           string = res2.str;
         } else {
           fatal(MYNAME ": Must select language code, %s and %s found.\n", res1.lc.constData(), res2.lc.constData());
@@ -729,11 +729,11 @@ GarminGPIFormat::wdata_compute_size(writer_data_t* data) const
 #if 0
         wpt->shortname.truncate(pidx);
 #endif
-      } else if ((opt_speed) && (!wpt->speed_has_value())) {
+      } else if (opt_speed && (!wpt->speed_has_value())) {
         wpt->set_speed(defspeed);
       }
 
-      if ((opt_proximity) && (!wpt->proximity_has_value())) {
+      if (opt_proximity && (!wpt->proximity_has_value())) {
         wpt->set_proximity(defproximity);
       }
 
@@ -976,11 +976,11 @@ skip_empty_block:
 }
 
 void
-GarminGPIFormat::write_category(const char* /*unused*/, const unsigned char* image, const int image_sz) const
+GarminGPIFormat::write_category(const QString& /*unused*/, const unsigned char* image, const int image_sz) const
 {
   int sz = wdata_compute_size(wdata);
   sz += 8;  /* string header */
-  sz += str_from_unicode(opt_cat.get()).size();
+  sz += str_from_unicode(opt_cat).size();
 
   gbfputint32(0x80009, fout);
   if ((! opt_hide_bitmap) && image_sz) {
@@ -989,7 +989,7 @@ GarminGPIFormat::write_category(const char* /*unused*/, const unsigned char* ima
     gbfputint32(sz, fout);
   }
   gbfputint32(sz, fout);
-  write_string(str_from_unicode(opt_cat.get()), 1);
+  write_string(str_from_unicode(opt_cat), 1);
 
   wdata_write(wdata);
 
@@ -1052,7 +1052,7 @@ GarminGPIFormat::enum_waypt_cb(const Waypoint* ref) const
 }
 
 void
-GarminGPIFormat::load_bitmap_from_file(const char* fname, const unsigned char** data, int* data_sz)
+GarminGPIFormat::load_bitmap_from_file(const QString& fname, const unsigned char** data, int* data_sz)
 {
   int i;
   int sz;
@@ -1207,6 +1207,19 @@ GarminGPIFormat::load_bitmap_from_file(const char* fname, const unsigned char**
   gbfclose(f);
 }
 
+char GarminGPIFormat::parse_units(const QString& str)
+{
+  char result;
+  if (str.startsWith('m', Qt::CaseInsensitive)) {
+    result = 'm';
+  } else if (str.startsWith('s', Qt::CaseInsensitive)) {
+    result = 's';
+  } else {
+    fatal(MYNAME ": Unknown units parameter (%s).\n", qPrintable(str));
+  }
+  return result;
+}
+
 /*******************************************************************************
 * %%%        global callbacks called by gpsbabel main process              %%% *
 *******************************************************************************/
@@ -1228,10 +1241,7 @@ GarminGPIFormat::rd_init(const QString& fname)
     fatal(MYNAME ": Unsupported code page (%d). File is likely encrypted.\n", codepage);
   }
 
-  units = tolower(opt_units[0]);
-  if ((units != 'm') && (units != 's')) {
-    fatal(MYNAME ": Unknown units parameter (%c).\n", opt_units[0]);
-  }
+  units = parse_units(opt_units);
 }
 
 void
@@ -1263,28 +1273,25 @@ GarminGPIFormat::wr_init(const QString& fname)
   codepage = 0;
 
   for (int i = 1250; i <= 1257; i++) {
-    if (QStringLiteral("windows-%1").arg(i).compare(QString(opt_writecodec), Qt::CaseInsensitive) == 0) {
+    if (QStringLiteral("windows-%1").arg(i).compare(opt_writecodec, Qt::CaseInsensitive) == 0) {
       codepage = i;
       break;
     }
   }
   if (! codepage) {
-    if (QString("utf8").compare(QString(opt_writecodec), Qt::CaseInsensitive) == 0) {
+    if (QStringLiteral("utf8").compare(opt_writecodec, Qt::CaseInsensitive) == 0) {
       codepage = 65001;
     }
   }
 
   if (! codepage) {
-    warning(MYNAME ": Unsupported character set (%s)!\n", qPrintable(opt_writecodec.get()));
+    warning(MYNAME ": Unsupported character set (%s)!\n", qPrintable(opt_writecodec));
     fatal(MYNAME ": Valid values are windows-1250 to windows-1257 and utf8.\n");
   }
 
-  codec = get_codec(opt_writecodec.getba());
+  codec = get_codec(opt_writecodec.get().toUtf8());
 
-  units = tolower(opt_units[0]);
-  if ((units != 'm') && (units != 's')) {
-    fatal(MYNAME ": Unknown units parameter (%c).\n", opt_units[0]);
-  }
+  units = parse_units(opt_units);
 
   alerts = (opt_alerts) ? 1 : 0;
 
@@ -1327,8 +1334,8 @@ GarminGPIFormat::wr_deinit()
   short_h = nullptr;
   gbfclose(fout);
 
-  if ((opt_sleep) && !gpsbabel_testmode()) {  /* don't sleep during 'testo' */
-    int sleep = xstrtoi(opt_sleep, nullptr, 10);
+  if (opt_sleep && !gpsbabel_testmode()) {  /* don't sleep during 'testo' */
+    int sleep = opt_sleep.get_result();
     if (sleep < 1) {
       sleep = 1;
     }
@@ -1359,14 +1366,14 @@ GarminGPIFormat::write()
   const unsigned char* image;
   int image_sz;
 
-  if (strlen(opt_cat) == 0) {
+  if (opt_cat.isEmpty()) {
     fatal(MYNAME ": Can't write empty category!\n");
   }
 
   if (opt_hide_bitmap) {
     image = nullptr;
     image_sz = 0;
-  } else if (opt_bitmap && *opt_bitmap) {
+  } else if (!opt_bitmap.isEmpty()) {
     load_bitmap_from_file(opt_bitmap, &image, &image_sz);
   } else {
     image = gpi_bitmap;  /* embedded GPSBabel icon in gpi format */
index 0efbf81b70c58172f55fb2cb49c74c623d23a09f..8b380c8365f6b4384b612872a585a625621c261a 100644 (file)
@@ -60,7 +60,7 @@
 #include "garmin_fs.h"  // for garmin_fs_t
 #include "gbfile.h"     // for gbfile
 #include "mkshort.h"    // for MakeShort
-#include "option.h"     // for OptionCString, OptionBool
+#include "option.h"     // for OptionString, OptionBool
 
 
 class GarminGPIFormat : public Format
@@ -299,29 +299,30 @@ private:
   void wdata_check(writer_data_t* data) const;
   int wdata_compute_size(writer_data_t* data) const;
   void wdata_write(const writer_data_t* data) const;
-  void write_category(const char* unused, const unsigned char* image, int image_sz) const;
+  void write_category(const QString& unused, const unsigned char* image, int image_sz) const;
   void write_header() const;
   void enum_waypt_cb(const Waypoint* ref) const;
-  static void load_bitmap_from_file(const char* fname, const unsigned char** data, int* data_sz);
+  static void load_bitmap_from_file(const QString& fname, const unsigned char** data, int* data_sz);
+  static char parse_units(const QString& str);
   QByteArray str_from_unicode(const QString& qstr) const {return codec->fromUnicode(qstr);}
   QString str_to_unicode(const QByteArray& cstr) const {return codec->toUnicode(cstr);}
 
   /* Data Members */
 
-  OptionCString opt_cat;
+  OptionString opt_cat;
   OptionBool opt_pos;
   OptionBool opt_notes;
   OptionBool opt_hide_bitmap;
   OptionBool opt_descr;
-  OptionCString opt_bitmap;
+  OptionString opt_bitmap;
   OptionBool opt_unique;
   OptionBool opt_alerts;
-  OptionCString opt_units;
-  OptionCString opt_speed;
-  OptionCString opt_proximity;
-  OptionCString opt_sleep;
-  OptionCString opt_lang;
-  OptionCString opt_writecodec;
+  OptionString opt_units;
+  OptionDouble opt_speed;
+  OptionDouble opt_proximity;
+  OptionInt opt_sleep;
+  OptionString opt_lang;
+  OptionString opt_writecodec;
   double defspeed{}, defproximity{};
   int alerts{};
 
@@ -356,7 +357,7 @@ private:
     },
     {
       "proximity", &opt_proximity, "Default proximity",
-      nullptr, ARGTYPE_STRING, ARG_NOMINMAX, nullptr
+      nullptr,  ARGTYPE_ALLOW_TRAILING_DATA | ARGTYPE_STRING, ARG_NOMINMAX, nullptr
     },
     {
       "sleep", &opt_sleep, "After output job done sleep n second(s)",
@@ -364,7 +365,7 @@ private:
     },
     {
       "speed", &opt_speed, "Default speed",
-      nullptr, ARGTYPE_STRING, ARG_NOMINMAX, nullptr
+      nullptr,  ARGTYPE_ALLOW_TRAILING_DATA | ARGTYPE_STRING, ARG_NOMINMAX, nullptr
     },
     {
       "unique", &opt_unique, "Create unique waypoint names (default = yes)",
index e52a681985a61b63d556c5be1df185832c278b81..e9a7bfdc93ab8b9a6225cec1471315530b7a3543 100644 (file)
@@ -79,24 +79,15 @@ bool GarminTxtFormat::is_valid_alt(double alt)
 
 /* helpers */
 
-const char*
-GarminTxtFormat::get_option_val(const char* option, const char* def)
-{
-  const char* c = (option != nullptr) ? option : def;
-  return c;
-}
-
 void
 GarminTxtFormat::init_date_and_time_format()
 {
   // This is old, and weird, code.. date_time_format is a global that's
   // explicitly malloced and freed elsewhere. This isn't very C++ at all,
   // but this format is on its deathbead for deprecation.
-  const char* d = get_option_val(opt_date_format, kDefaultDateFormat);
-  QString d1 = convert_human_date_format(d);
+  QString d1 = convert_human_date_format(opt_date_format);
 
-  const char* t = get_option_val(opt_time_format, kDefaultTimeFormat);
-  QString t1 = convert_human_time_format(t);
+  QString t1 = convert_human_time_format(opt_time_format);
 
   date_time_format = QStringLiteral("%1 %2").arg(d1, t1);
 }
@@ -625,13 +616,8 @@ GarminTxtFormat::track_disp_wpt_cb(const Waypoint* wpt)
 void
 GarminTxtFormat::garmin_txt_utc_option()
 {
-  if (opt_utc != nullptr) {
-    if (case_ignore_strcmp(opt_utc.get(), "utc") == 0) {
-      utc_offs = 0;
-    } else {
-      utc_offs = xstrtoi(opt_utc, nullptr, 10);
-    }
-    utc_offs *= (60 * 60);
+  if (opt_utc) {
+    utc_offs = opt_utc.get_result() * 60 * 60;
     gtxt_flags.utc = 1;
   }
 }
@@ -652,30 +638,27 @@ GarminTxtFormat::wr_init(const QString& fname)
   fout = new gpsbabel::TextStream;
   fout->open(fname, QIODevice::WriteOnly, MYNAME, "windows-1252");
 
-  gtxt_flags.metric = (toupper(*get_option_val(opt_dist, "m")) == 'M');
-  gtxt_flags.celsius = (toupper(*get_option_val(opt_temp, "c")) == 'C');
+  gtxt_flags.metric = opt_dist.get().startsWith("m", Qt::CaseInsensitive);
+  gtxt_flags.celsius = opt_temp.get().startsWith("c", Qt::CaseInsensitive);
   init_date_and_time_format();
   if (opt_precision) {
-    precision = xstrtoi(opt_precision, nullptr, 10);
+    precision = opt_precision.get_result();
     if (precision < 0) {
-      fatal(MYNAME ": Invalid precision (%s)!", qPrintable(opt_precision.get()));
+      fatal(MYNAME ": Invalid precision (%s)!", qPrintable(opt_precision));
     }
   }
 
-  QString datum_str = get_option_val(opt_datum, nullptr);
-  QString grid_str = get_option_val(opt_grid, nullptr);
-
   grid_index = grid_lat_lon_dmm;
-  if (!grid_str.isEmpty()) {
+  if (!opt_grid.isEmpty()) {
     bool ok;
 
-    if (int i = grid_str.toInt(&ok); ok) {
+    if (int i = opt_grid.toInt(&ok); ok) {
       grid_index = (grid_type) i;
       if ((grid_index < GRID_INDEX_MIN) || (grid_index > GRID_INDEX_MAX))
-        fatal(MYNAME ": Grid index out of range (%d..%d)!",
+        fatal(MYNAME ": Grid index out of range (%d..%d)!\n",
               (int)GRID_INDEX_MIN, (int)GRID_INDEX_MAX);
     } else {
-      grid_index = gt_lookup_grid_type(grid_str, MYNAME);
+      grid_index = gt_lookup_grid_type(opt_grid, MYNAME);
     }
   }
 
@@ -687,7 +670,7 @@ GarminTxtFormat::wr_init(const QString& fname)
     datum_index = kDatumWGS84;
     break;
   default:
-    datum_index = gt_lookup_datum_index(datum_str, MYNAME);
+    datum_index = gt_lookup_datum_index(opt_datum, MYNAME);
   }
 
   garmin_txt_utc_option();
index 9af5aa4421aaf3fedfe0d9ebe50189fdc8b15a4d..969255d75298e8ac4aeb1a87b2c8249912e95282 100644 (file)
@@ -36,7 +36,7 @@
 
 #include "defs.h"
 #include "format.h"               // for Format
-#include "option.h"               // for OptionCString
+#include "option.h"               // for OptionString
 #include "src/core/textstream.h"  // for TextStream
 
 
@@ -69,8 +69,6 @@ private:
   /* Constants */
 
   static constexpr double kGarminUnknownAlt = 1.0e25;
-  static constexpr char kDefaultDateFormat[] = "dd/mm/yyyy";
-  static constexpr char kDefaultTimeFormat[] = "HH:mm:ss";
 
   static const QVector<QString> headers;
 
@@ -111,7 +109,6 @@ private:
   /* Member Functions */
 
   static bool is_valid_alt(double alt);
-  static const char* get_option_val(const char* option, const char* def);
   void init_date_and_time_format();
   void convert_datum(const Waypoint* wpt, double* dest_lat, double* dest_lon) const;
   void enum_waypt_cb(const Waypoint* wpt);
@@ -172,23 +169,23 @@ private:
   std::array<QList<std::pair<QString, int>>, unknown_header> header_mapping_info;
   QStringList header_column_names;
 
-  OptionCString opt_datum;
-  OptionCString opt_dist;
-  OptionCString opt_temp;
-  OptionCString opt_date_format;
-  OptionCString opt_time_format;
-  OptionCString opt_precision;
-  OptionCString opt_utc;
-  OptionCString opt_grid;
+  OptionString opt_datum;
+  OptionString opt_dist;
+  OptionString opt_temp;
+  OptionString opt_date_format;
+  OptionString opt_time_format;
+  OptionInt opt_precision;
+  OptionInt opt_utc;
+  OptionString opt_grid;
 
   QVector<arglist_t> garmin_txt_args = {
-    {"date",  &opt_date_format, "Read/Write date format (i.e. yyyy/mm/dd)", nullptr, ARGTYPE_STRING, ARG_NOMINMAX, nullptr},
+    {"date",  &opt_date_format, "Read/Write date format (i.e. yyyy/mm/dd)", "dd/mm/yyyy", ARGTYPE_STRING, ARG_NOMINMAX, nullptr},
     {"datum", &opt_datum,          "GPS datum (def. WGS 84)", "WGS 84", ARGTYPE_STRING, ARG_NOMINMAX, nullptr},
     {"dist",  &opt_dist,        "Distance unit [m=metric, s=statute]", "m", ARGTYPE_STRING, ARG_NOMINMAX, nullptr},
     {"grid",  &opt_grid,        "Write position using this grid.", nullptr, ARGTYPE_STRING, ARG_NOMINMAX, nullptr},
     {"prec",  &opt_precision,   "Precision of coordinates", "3", ARGTYPE_INT, ARG_NOMINMAX, nullptr},
     {"temp",  &opt_temp,        "Temperature unit [c=Celsius, f=Fahrenheit]", "c", ARGTYPE_STRING, ARG_NOMINMAX, nullptr},
-    {"time",  &opt_time_format, "Read/Write time format (i.e. HH:mm:ss xx)", nullptr, ARGTYPE_STRING, ARG_NOMINMAX, nullptr},
+    {"time",  &opt_time_format, "Read/Write time format (i.e. HH:mm:ss xx)", "HH:mm:ss", ARGTYPE_STRING, ARG_NOMINMAX, nullptr},
     {"utc",   &opt_utc,         "Write timestamps with offset x to UTC time", nullptr, ARGTYPE_INT, "-23", "+23", nullptr},
   };
 
index 6f96b2b130296d6259b33595eb7b8764368165d7..8e221139ebf37d4719f2b72cbd3d0055679230ea 100644 (file)
@@ -74,7 +74,7 @@ GarminXTFormat::format_garmin_xt_rd_st_attrs(char* p_trk_name, uint8_t* p_track_
 
   // get the option for the processing the track name
   if (opt_trk_header) {
-    method = xstrtoi(opt_trk_header, nullptr, 10);
+    method = opt_trk_header.get_result();
     // if method is out of range set to default
     if ((method < 0) || (method > 1)) {
       method = 0;
@@ -315,7 +315,7 @@ GarminXTFormat::format_garmin_xt_proc_atrk()
 
   // get the option for the processing the track name
   if (opt_trk_header) {
-    method = xstrtoi(opt_trk_header, nullptr, 10);
+    method = opt_trk_header.get_result();
   }
 
   if (! track) {
@@ -373,7 +373,7 @@ void
 GarminXTFormat::read()
 {
   // Saved Tracks file
-  if (strcmp(opt_xt_ftype, "STRK") == 0) {
+  if (opt_xt_ftype.get() == "STRK") {
     format_garmin_xt_proc_strk();
   } else { // Active Track file
     format_garmin_xt_proc_atrk();
index 645b9928b3448c9089e2c113da7e451ddd71109a..5b750db3f1d311675012e647cb0f3cb4b6ae0b70 100644 (file)
@@ -34,7 +34,7 @@
 #include "defs.h"
 #include "format.h"  // for Format
 #include "gbfile.h"  // for gbfile
-#include "option.h"  // for OptionCString
+#include "option.h"  // for OptionString
 
 
 class GarminXTFormat : public Format
@@ -94,8 +94,8 @@ private:
 
   gbfile* fin{};
   route_head* track{};
-  OptionCString        opt_xt_ftype;
-  OptionCString        opt_trk_header;
+  OptionString opt_xt_ftype;
+  OptionInt    opt_trk_header;
 
   QVector<arglist_t> format_garmin_xt_args = {
     {"ftype", &opt_xt_ftype, "Garmin Mobile XT ([ATRK]/STRK)", "ATRK", ARGTYPE_STRING | ARGTYPE_REQUIRED, ARG_NOMINMAX, nullptr},
diff --git a/gdb.cc b/gdb.cc
index 3b024e063741e4b9ae5952b04df3fca5eb5087d3..95d4af2229b18c27f6e5265de93e0a144580a82d 100644 (file)
--- a/gdb.cc
+++ b/gdb.cc
 
 #include <cmath>                    // for fabs
 #include <cstdio>                   // for printf, SEEK_SET
-#include <cstdlib>                  // for strtol
 #include <cstring>                  // for memset, strstr, strcmp
 #include <iterator>                 // for next
 
-#include "defs.h"                   // for Waypoint, warning, route_head, fatal, UrlLink, bounds, UrlList, unknown_alt, xfree, waypt_add_to_bounds, waypt_init_bounds, xstrtoi, route_add_wpt, route_disp_all, waypt_bounds_valid, xmalloc, gb_color, WaypointList, find_wa...
+#include "defs.h"                   // for Waypoint, warning, route_head, fatal, UrlLink, bounds, UrlList, unknown_alt, xfree, waypt_add_to_bounds, waypt_init_bounds, route_add_wpt, route_disp_all, waypt_bounds_valid, xmalloc, gb_color, WaypointList, find_wa...
 #include "formspec.h"               // for FormatSpecificDataList
 #include "garmin_fs.h"              // for garmin_fs_t, garmin_ilink_t
 #include "garmin_tables.h"          // for gt_waypt_class_map_point, gt_color_index_by_rgb, gt_color_value, gt_waypt_classes_e, gt_find_desc_from_icon_number, gt_find_icon_number_from_desc, gt_gdb_display_mode_symbol, gt_get_icao_country, gt_waypt_class_user_waypoint, GDB, gt_display_mode_symbol
@@ -1635,8 +1634,8 @@ GdbFormat::wr_init(const QString& fname)
   fout = gbfopen_le(fname, "wb", MYNAME);
   ftmp = gbfopen_le(nullptr, "wb", MYNAME);
 
-  gdb_category = (gdb_opt_category) ? xstrtoi(gdb_opt_category, nullptr, 10) : 0;
-  gdb_ver = (gdb_opt_ver && *gdb_opt_ver) ? xstrtoi(gdb_opt_ver, nullptr, 10) : 0;
+  gdb_category = gdb_opt_category ? gdb_opt_category.get_result() : 0;
+  gdb_ver = gdb_opt_ver.get_result();
 
   if (gdb_category) {
     if ((gdb_category < 1) || (gdb_category > 16)) {
@@ -1646,7 +1645,7 @@ GdbFormat::wr_init(const QString& fname)
   }
 
   if (gdb_opt_bitcategory) {
-    gdb_category = strtol(gdb_opt_bitcategory, nullptr, 0);
+    gdb_category = gdb_opt_bitcategory.get_result();
   }
 
   waypt_nameposn_out_hash.clear();
@@ -1674,9 +1673,6 @@ GdbFormat::wr_deinit()
 void
 GdbFormat::write()
 {
-  if (gdb_opt_ver) {
-    gdb_ver = xstrtoi(gdb_opt_ver, nullptr, 10);
-  }
   write_header();
 
   reset_short_handle("WPT");
diff --git a/gdb.h b/gdb.h
index 87920c888b7e0c1d29f8109d8069989c3346bd85..a2bcb968020aba85acff093f460a7be53515524d 100644 (file)
--- a/gdb.h
+++ b/gdb.h
@@ -38,7 +38,7 @@
 #include "garmin_tables.h"  // for gt_waypt_classes_e
 #include "gbfile.h"         // for gbfile
 #include "mkshort.h"        // for MakeShort
-#include "option.h"         // for OptionBool, OptionCString
+#include "option.h"         // for OptionBool, OptionString
 
 
 class GdbFormat : public Format
@@ -162,11 +162,11 @@ private:
   WptNamePosnHash waypt_nameposn_out_hash;
   MakeShort* short_h{};
 
-  OptionCString gdb_opt_category;
-  OptionCString gdb_opt_ver;
+  OptionInt gdb_opt_category;
+  OptionInt gdb_opt_ver;
   OptionBool gdb_opt_via;
   OptionBool gdb_opt_roadbook;
-  OptionCString gdb_opt_bitcategory;
+  OptionInt gdb_opt_bitcategory;
   OptionBool gdb_opt_drop_hidden_wpt;
 
   int waypt_flag{};
@@ -187,7 +187,7 @@ private:
     },
     {
       "bitscategory", &gdb_opt_bitcategory, "Bitmap of categories",
-      nullptr, ARGTYPE_INT, "1", "65535", nullptr
+      nullptr, ARGTYPE_BASE_AUTO | ARGTYPE_INT, "1", "65535", nullptr
     },
     {
       "ver", &gdb_opt_ver,
diff --git a/geo.cc b/geo.cc
index caf8b674a2b207f9150d9d415392093f96b10eb7..15eab8623a44b8b7d0ed8636ec927769e8c6234a 100644 (file)
--- a/geo.cc
+++ b/geo.cc
@@ -149,7 +149,7 @@ void GeoFormat::geo_waypt_pr(const Waypoint* waypointp, QXmlStreamWriter& writer
   writer.writeAttribute(QStringLiteral("lon"), QString::number(waypointp->longitude, 'f'));
   writer.writeEndElement();
 
-  writer.writeTextElement(QStringLiteral("type"), deficon ? deficon.get() : waypointp->icon_descr);
+  writer.writeTextElement(QStringLiteral("type"), deficon ? deficon : waypointp->icon_descr);
 
   if (waypointp->HasUrlLink()) {
     writer.writeStartElement(QStringLiteral("link"));
diff --git a/geo.h b/geo.h
index 785d0c4f0899e4337a4827f0b1c3be11d85bc040..8267cfc4c1a5a9ff1820629e6e08f3d5bb27402f 100644 (file)
--- a/geo.h
+++ b/geo.h
@@ -27,7 +27,7 @@
 #include "defs.h"
 #include "format.h"          // for Format
 #include "geocache.h"        // for Geocache, Geocache::container_t
-#include "option.h"          // for OptionBool, OptionCString
+#include "option.h"          // for OptionBool, OptionString
 
 
 class GeoFormat : public Format
@@ -67,7 +67,7 @@ private:
 
   /* Data Members */
 
-  OptionCString deficon;
+  OptionString deficon;
 
   QVector<arglist_t> geo_args = {
     {"deficon", &deficon, "Default icon name", nullptr, ARGTYPE_STRING, ARG_NOMINMAX, nullptr },
index c4a731b6740c5c9e8382711708422d6130a3ab19..6a192b6d6424fda1c48785eddd5dc0b839978e94 100644 (file)
@@ -268,12 +268,12 @@ GlobalsatSportFormat::rd_init(const QString& fname)
     printf(MYNAME " rd_init()\n");
   }
   if (opt_dump_file) {
-    dumpfile = gbfopen(opt_dump_file.get(), "wb", MYNAME);
+    dumpfile = gbfopen(opt_dump_file, "wb", MYNAME);
     if (!dumpfile) {
-      printf(MYNAME " rd_init() creating dumpfile %s FAILED continue anyway\n", qPrintable(opt_dump_file.get()));
+      printf(MYNAME " rd_init() creating dumpfile %s FAILED continue anyway\n", qPrintable(opt_dump_file));
     } else {
       if (global_opts.debug_level > 1) {
-        printf(MYNAME " rd_init() creating dumpfile %s for writing binary copy of serial stream\n", qPrintable(opt_dump_file.get()));
+        printf(MYNAME " rd_init() creating dumpfile %s for writing binary copy of serial stream\n", qPrintable(opt_dump_file));
       }
     }
   }
@@ -288,11 +288,11 @@ GlobalsatSportFormat::rd_init(const QString& fname)
 
   }
   if (opt_timezone) {
-    if (QTimeZone::isTimeZoneIdAvailable(opt_timezone.getba())) {
-      timezn = new QTimeZone(opt_timezone.getba());
+    if (QTimeZone::isTimeZoneIdAvailable(opt_timezone.get().toUtf8())) {
+      timezn = new QTimeZone(opt_timezone.get().toUtf8());
     } else {
       list_timezones();
-      fatal(MYNAME ": Requested time zone \"%s\" not available.\n", qPrintable(opt_timezone.get()));
+      fatal(MYNAME ": Requested time zone \"%s\" not available.\n", qPrintable(opt_timezone));
     }
   } else {
     timezn = nullptr;
index 374a537cbb3d9e889522175ab72884645586964e..bdebca4c5fac1056aec827ac6886e3ac9d2cd9e0 100644 (file)
@@ -47,7 +47,7 @@
 #include "defs.h"
 #include "format.h"   // for Format
 #include "gbfile.h"   // for gbfclose, gbfopen, gbfread, gbfwrite, gbfile
-#include "option.h"   // for OptionCString, OptionBool
+#include "option.h"   // for OptionString, OptionBool
 
 
 class GlobalsatSportFormat : public Format
@@ -231,9 +231,9 @@ private:
 
   OptionBool showlist;                  // if true show a list instead of download tracks
 
-  OptionCString opt_dump_file;          // dump raw data to this file (optional)
+  OptionString opt_dump_file;          // dump raw data to this file (optional)
   OptionBool opt_input_dump_file;       // if true input is from a dump-file instead of serial console
-  OptionCString opt_timezone;
+  OptionString opt_timezone;
   gbfile* dumpfile{nullptr};             // used for creating bin/RAW datadump files, useful for testing
   gbfile* in_file{nullptr};              // used for reading from bin/RAW datadump files, useful for testing
   QTimeZone* timezn{nullptr};
diff --git a/gpx.cc b/gpx.cc
index 5bc029e499f9dc684c187a6051046e183a42d5f9..e65f1ef74cfae050d438139b3e2cc429a53bb230 100644 (file)
--- a/gpx.cc
+++ b/gpx.cc
@@ -25,7 +25,7 @@
 #include <cmath>                            // for lround
 #include <cstdio>                           // for sscanf
 #include <cstdint>                          // for uint16_t
-#include <cstring>                          // for strchr, strncpy
+#include <cstring>                          // for strchr
 #include <optional>                         // for optional
 #include <utility>                          // for as_const
 
@@ -102,7 +102,7 @@ GpxFormat::gpx_reset_short_handle()
     mkshort_handle->set_whitespace_ok(false);
   }
 
-  mkshort_handle->set_length(xstrtoi(snlen, nullptr, 10));
+  mkshort_handle->set_length(snlen.get_result());
 }
 
 void
@@ -985,7 +985,7 @@ GpxFormat::wr_init(const QString& fname)
   * available use it, otherwise use the default.
   */
 
-  if (opt_gpxver != nullptr) {
+  if (opt_gpxver) {
     gpx_write_version = QVersionNumber::fromString(opt_gpxver.get()).normalized();
   } else if (!gpx_highest_version_read.isNull()) {
     gpx_write_version = gpx_highest_version_read;
@@ -1703,7 +1703,7 @@ void
 GpxFormat::write()
 {
 
-  elevation_precision = xstrtoi(opt_elevation_precision, nullptr, 10);
+  elevation_precision = opt_elevation_precision.get_result();
 
   gpx_reset_short_handle();
   auto gpx_waypt_pr_lambda = [this](const Waypoint* waypointp)->void {
diff --git a/gpx.h b/gpx.h
index 99a1567e264a481133cbb870794020070ca084b4..2fe36b2d5db68bcb2955da94e75d1b13a0001899 100644 (file)
--- a/gpx.h
+++ b/gpx.h
@@ -35,7 +35,7 @@
 #include "format.h"                    // for Format
 #include "formspec.h"                  // for FormatSpecificData
 #include "mkshort.h"                   // for MakeShort
-#include "option.h"                    // for OptionBool, OptionCString
+#include "option.h"                    // for OptionBool, OptionString
 #include "src/core/file.h"             // for File
 #include "src/core/xmlstreamwriter.h"  // for XmlStreamWriter
 #include "src/core/xmltag.h"           // for xml_tag
@@ -258,7 +258,7 @@ private:
   OptionBool opt_logpoint;
   OptionBool opt_humminbirdext;
   OptionBool opt_garminext;
-  OptionCString opt_elevation_precision;
+  OptionInt opt_elevation_precision;
   int logpoint_ct = 0;
   int elevation_precision{};
 
@@ -266,7 +266,7 @@ private:
   const QVersionNumber gpx_1_0 = QVersionNumber(1,0).normalized();
   const QVersionNumber gpx_1_1 = QVersionNumber(1,1).normalized();
   QVersionNumber gpx_highest_version_read;
-  OptionCString opt_gpxver;
+  OptionString opt_gpxver;
   QVersionNumber gpx_write_version;
   QXmlStreamAttributes gpx_namespace_attribute;
 
@@ -285,9 +285,9 @@ private:
   QString link_type;
 
 
-  OptionCString snlen;
+  OptionInt snlen;
   OptionBool suppresswhite;
-  OptionCString urlbase;
+  OptionString urlbase;
   route_head* trk_head{};
   route_head* rte_head{};
   const route_head* current_trk_head{};                // Output.
index d7a5b7838fc118f45d8e925f74dd64715da76d97..65e866adf36ccbbc57fe688d57a404423e3a2f3a 100644 (file)
@@ -79,7 +79,7 @@ GtrnctrFormat::wr_init(const QString& fname)
 
   if (opt_sport) {
     for (unsigned int i = 0; i < std::size(gtc_sportlist); i++) {
-      if (0 == case_ignore_strncmp(opt_sport.get(), gtc_sportlist[i], 2)) {
+      if (0 == case_ignore_strncmp(opt_sport, gtc_sportlist[i], 2)) {
         gtc_sport = i;
         break;
       }
index 1627f1473a377c812cdd8847235e558d226cb471..9873e022933cffe9131abf1c4bc98616b56a62bc 100644 (file)
--- a/gtrnctr.h
+++ b/gtrnctr.h
@@ -35,7 +35,7 @@
 #include "defs.h"                // for arglist_t, ff_cap, route_head, Waypoint, computed_trkdata, ARG_NOMINMAX, ff_cap_read, ARGTYPE_BOOL, ARGTYPE_STRING, ff_cap_none, ff_cap_write, ff_type, ff_type_file
 #include "format.h"              // for Format
 #include "gbfile.h"              // for gbfile
-#include "option.h"              // for OptionBool, OptionCString
+#include "option.h"              // for OptionBool, OptionString
 #include "src/core/datetime.h"   // for DateTime
 #include "xmlgeneric.h"          // for cb_cdata, xg_functor_map_entry, cb_start, cb_end
 
@@ -133,7 +133,7 @@ private:
   double gtc_end_lat{};
   double gtc_end_long{};
 
-  OptionCString opt_sport;
+  OptionString opt_sport;
   OptionBool opt_course;
 
   QVector<arglist_t> gtc_args = {
index c7d56414ef1f377dc51018e3b565962deba3445f..778424afb449c2e1f2f1142d37eac4fee8d35873 100644 (file)
--- a/height.cc
+++ b/height.cc
@@ -83,7 +83,7 @@ void HeightFilter::correct_height(const Waypoint* wpt)
   auto* waypointp = const_cast<Waypoint*>(wpt);
 
   if (waypointp->altitude != unknown_alt) {
-    if (addopt != nullptr) {
+    if (addopt) {
       waypointp->altitude += addf;
     }
 
@@ -96,7 +96,7 @@ void HeightFilter::correct_height(const Waypoint* wpt)
 void HeightFilter::init()
 {
   addf = 0.0;
-  if (addopt != nullptr) {
+  if (addopt) {
     if (parse_distance(addopt, &addf, 1.0, MYNAME) == 0) {
       fatal(MYNAME ": No height specified with add option.");
     }
index 8b3333076faf3603248a47f976599838abf1ff7f..9aedb58eb99232100b4663887101b9ed4d8e770b 100644 (file)
--- a/height.h
+++ b/height.h
@@ -32,7 +32,7 @@
 
 #include "defs.h"    // for arglist_t, ARG_NOMINMAX, ARGTYPE_BEGIN_REQ, ARGTYPE_BOOL, ARGTYPE_END_REQ, ARGTYPE_FLOAT, Waypoint
 #include "filter.h"  // for Filter
-#include "option.h"  // for OptionBool, OptionCString
+#include "option.h"  // for OptionBool, OptionString
 
 #if FILTERS_ENABLED
 
@@ -47,7 +47,7 @@ public:
   void process() override;
 
 private:
-  OptionCString addopt;
+  OptionDouble addopt;
   OptionBool wgs84tomslopt;
   double addf{};
   // include static constexpr data member definitions with intializers for grid as private members.
@@ -56,7 +56,7 @@ private:
   QVector<arglist_t> args = {
     {
       "add", &addopt, "Adds a constant value to every altitude",
-      nullptr, ARGTYPE_BEGIN_REQ | ARGTYPE_FLOAT, ARG_NOMINMAX, nullptr
+      nullptr,  ARGTYPE_ALLOW_TRAILING_DATA | ARGTYPE_BEGIN_REQ | ARGTYPE_STRING, ARG_NOMINMAX, nullptr
     },
     {
       "wgs84tomsl", &wgs84tomslopt, "Converts WGS84 ellipsoidal height to orthometric height (MSL)",
diff --git a/html.cc b/html.cc
index e9971ed2604eecc89908e922cdf561a15b83fb8f..5df1998a4a968ae7d78f7562cadcc12064891190 100644 (file)
--- a/html.cc
+++ b/html.cc
@@ -23,6 +23,8 @@
 
 #include <QChar>                   // for QChar
 #include <QIODevice>               // for QIODevice, QIODevice::WriteOnly
+#include <QRegularExpression>      // for QRegularExpression
+#include <QRegularExpressionMatch> // for QRegularExpressionMatch
 #include <QString>                 // for QString, operator!=
 #include <QTextStream>             // for QTextStream
 #include <Qt>                      // for CaseInsensitive
@@ -47,6 +49,23 @@ HtmlFormat::wr_init(const QString& fname)
   file_out = new gpsbabel::TextStream;
   file_out->open(fname, QIODevice::WriteOnly, MYNAME);
   mkshort_handle = new MakeShort;
+
+  static const QRegularExpression re("^(?:ddd|dmm|dms)$");
+  assert(re.isValid());
+  if (re.match(opt_degformat).hasMatch()) {
+    degformat = opt_degformat.get().at(2).toLatin1();
+  } else {
+    fatal(MYNAME ": Unrecognized degformat %s, expected 'ddd', 'dmm' or 'dms'.\n", qPrintable(opt_degformat));
+  }
+
+  if (opt_altunits.get().startsWith('f')) {
+    altunits = 'f';
+  } else if (opt_altunits.get().startsWith('m')) {
+    altunits = 'm';
+  } else {
+    fatal(MYNAME ": Unrecognized altunits %s, expected 'f' for feet or 'm' for meters.\n", qPrintable(opt_altunits));
+  }
+
 }
 
 void
@@ -85,14 +104,14 @@ HtmlFormat::html_disp(const Waypoint* wpt) const
   *file_out << "        <td>\n";
   *file_out << "          <p class=\"gpsbabelwaypoint\">" << sn << " - ";
   *file_out << QStringLiteral("%1 (%2%3 %4 %5)")
-            .arg(pretty_deg_format(wpt->latitude, wpt->longitude, degformat[2], " ", true))
+            .arg(pretty_deg_format(wpt->latitude, wpt->longitude, degformat, " ", true))
             .arg(utmz)
             .arg(utmzc)
             .arg(utme, 6, 'f', 0)
             .arg(utmn, 7, 'f', 0);
   if (wpt->altitude != unknown_alt) {
     *file_out << QStringLiteral(" alt:%1")
-              .arg((int)((altunits[0]=='f') ? METERS_TO_FEET(wpt->altitude) : wpt->altitude));
+              .arg((int)((altunits == 'f') ? METERS_TO_FEET(wpt->altitude) : wpt->altitude));
   }
   *file_out << "<br>\n";
   if (wpt->description != wpt->shortname) {
@@ -178,7 +197,7 @@ HtmlFormat::html_disp(const Waypoint* wpt) const
           double lat = logpart->xml_attribute("lat").toDouble();
           double lon = logpart->xml_attribute("lon").toDouble();
           *file_out << "<span class=\"gpsbabellogcoords\">"
-                    << pretty_deg_format(lat, lon, degformat[2], " ", true) << "</span><br>\n";
+                    << pretty_deg_format(lat, lon, degformat, " ", true) << "</span><br>\n";
         }
 
         logpart = curlog->xml_findfirst(u"groundspeak:text");
@@ -232,9 +251,9 @@ HtmlFormat::write()
               << gpsbabel_version << "\">\n";
   }
   *file_out << "  <title>GPSBabel HTML Output</title>\n";
-  if (stylesheet) {
+  if (opt_stylesheet) {
     *file_out << R"(  <link rel="stylesheet" type="text/css" href=")"
-              << stylesheet << "\">\n";
+              << opt_stylesheet.get() << "\">\n";
   } else {
     *file_out << "  <style>\n";
     *file_out << "    p.gpsbabelwaypoint { font-size: 120%; font-weight: bold }\n";
diff --git a/html.h b/html.h
index a32a6b35a7abc971896ea71b5c11133c4c466981..faf464ccab4b6c9f296f604f8866b057643cd9fd 100644 (file)
--- a/html.h
+++ b/html.h
@@ -28,7 +28,7 @@
 #include "defs.h"
 #include "format.h"               // for Format
 #include "mkshort.h"              // for MakeShort
-#include "option.h"               // for OptionCString, OptionBool
+#include "option.h"               // for OptionString, OptionBool
 #include "src/core/textstream.h"  // for TextStream
 
 
@@ -69,15 +69,17 @@ private:
 
   int waypoint_number{};
 
-  OptionCString stylesheet;
+  OptionString opt_stylesheet;
   OptionBool html_encrypt;
   OptionBool includelogs;
-  OptionCString degformat;
-  OptionCString altunits;
+  OptionString opt_degformat;
+  OptionString opt_altunits;
+  char degformat{};
+  char altunits {};
 
   QVector<arglist_t> html_args = {
     {
-      "stylesheet", &stylesheet,
+      "stylesheet", &opt_stylesheet,
       "Path to HTML style sheet", nullptr, ARGTYPE_STRING, ARG_NOMINMAX, nullptr
     },
     {
@@ -89,11 +91,11 @@ private:
       "Include groundspeak logs if present", nullptr, ARGTYPE_BOOL, ARG_NOMINMAX, nullptr
     },
     {
-      "degformat", &degformat,
+      "degformat", &opt_degformat,
       "Degrees output as 'ddd', 'dmm'(default) or 'dms'", "dmm", ARGTYPE_STRING, ARG_NOMINMAX, nullptr
     },
     {
-      "altunits", &altunits,
+      "altunits", &opt_altunits,
       "Units for altitude (f)eet or (m)etres", "m", ARGTYPE_STRING, ARG_NOMINMAX, nullptr
     },
   };
diff --git a/igc.cc b/igc.cc
index 49595d03e4c6304ffba1fbae000f065bbde3c4d7..c414e07bc0d3a442e6c50b2515b783ac85e7e3b5 100644 (file)
--- a/igc.cc
+++ b/igc.cc
@@ -947,10 +947,10 @@ void IgcFormat::wr_track()
   // If both found, attempt to merge them
   if (pres_track && gnss_track) {
     if (timeadj) {
-      if (strcmp(timeadj, "auto") == 0) {
+      if (timeadj.get() == "auto") {
         time_adj = correlate_tracks(pres_track, gnss_track);
-      } else if (sscanf(timeadj, "%d", &time_adj) != 1) {
-        fatal(MYNAME ": bad timeadj argument '%s'\n", qPrintable(timeadj.get()));
+      } else {
+        time_adj = timeadj.toInt();
       }
     } else {
       time_adj = 0;
diff --git a/igc.h b/igc.h
index f7ff4fe527445bf5ae0b00988ae05f8c8b62867d..edda6616e15c62e8ba1f56134d580f2a62902dc7 100644 (file)
--- a/igc.h
+++ b/igc.h
@@ -42,7 +42,7 @@
 #include "formspec.h"           // for FormatSpecificData, kFsIGC
 #include "gbfile.h"             // for gbfprintf, gbfclose, gbfopen, gbfputs, gbfgetstr, gbfile
 #include "kml.h"                // for wp_field
-#include "option.h"             // for OptionBool, OptionCString
+#include "option.h"             // for OptionBool, OptionString
 #include "src/core/datetime.h"  // for DateTime
 
 /*
@@ -294,7 +294,7 @@ private:
   gbfile* file_out{};
   char manufacturer[4] {};
   const route_head* head{};
-  OptionCString timeadj;
+  OptionString timeadj;
 
   QVector<arglist_t> igc_args = {
     {
index 577e80ff3b0edbc124d0baaa6640526d933931d0..22b851e37647b4c7864808dc9257917dfc1c0868 100644 (file)
@@ -23,7 +23,7 @@
 
 #include <climits>              // for INT_MAX
 #include <cmath>                // for ceil, isfinite
-#include <cstdlib>              // for abs, strtod
+#include <cstdlib>              // for abs
 #include <optional>             // for optional
 
 #include <QString>              // for QString
@@ -84,13 +84,13 @@ void InterpolateFilter::process_rte(route_head* rte)
 
       // How many points need to be inserted?
       double npts = 0;
-      if (opt_time != nullptr) {
+      if (opt_time) {
         if (!timespan.has_value()) {
           fatal(FatalMsg() << MYNAME ": points must have valid times to interpolate by time!");
         }
         // interpolate even if time is running backwards.
         npts = std::abs(*timespan) / max_time_step;
-      } else if (opt_dist != nullptr) {
+      } else if (opt_dist) {
         double distspan = radtometers(gcdist(pos1, wpt->position()));
         npts = distspan / max_dist_step;
       }
@@ -142,16 +142,16 @@ void InterpolateFilter::process_rte(route_head* rte)
 
 void InterpolateFilter::init()
 {
-  if ((opt_time != nullptr) && (opt_dist != nullptr)) {
+  if (opt_time && opt_dist) {
     fatal(FatalMsg() << MYNAME ": Can't interpolate on both time and distance.");
-  } else if ((opt_time != nullptr) && opt_route) {
+  } else if (opt_time && opt_route) {
     fatal(FatalMsg() << MYNAME ": Can't interpolate routes on time.");
-  } else if (opt_time != nullptr) {
-    max_time_step = 1000 * strtod(opt_time, nullptr); // milliseconds
+  } else if (opt_time) {
+    max_time_step = 1000 * opt_time.get_result(); // milliseconds
     if (max_time_step <= 0) {
       fatal(FatalMsg() << MYNAME ": interpolation time should be positive!");
     }
-  } else if (opt_dist != nullptr) {
+  } else if (opt_dist) {
     if (parse_distance(opt_dist, &max_dist_step, kMetersPerMile, MYNAME) == 0) {
       fatal(FatalMsg() << MYNAME ": no distance specified with distance option!");
     }
index 269a5d23ed98f4b8ebb4e756b38abfaf44b60368..a0fd4cafd517b0a539e7eefa4a6cf7c62b49cc38 100644 (file)
@@ -28,7 +28,7 @@
 
 #include "defs.h"    // for ARG_NOMINMAX, arglist_t, ARGTYPE_BEGIN_EXCL, ARG...
 #include "filter.h"  // for Filter
-#include "option.h"  // for OptionCString, OptionBool
+#include "option.h"  // for OptionString, OptionBool
 
 #if FILTERS_ENABLED
 
@@ -49,9 +49,9 @@ private:
 
   /* Data Members */
 
-  OptionCString opt_time;
+  OptionDouble opt_time;
   double max_time_step{0};
-  OptionCString opt_dist;
+  OptionDouble opt_dist;
   double max_dist_step{0};
   OptionBool opt_route;
 
@@ -63,7 +63,7 @@ private:
     },
     {
       "distance", &opt_dist, "Distance interval",
-      nullptr, ARGTYPE_END_EXCL | ARGTYPE_END_REQ | ARGTYPE_STRING,
+      nullptr, ARGTYPE_END_EXCL | ARGTYPE_END_REQ | ARGTYPE_ALLOW_TRAILING_DATA | ARGTYPE_STRING,
       ARG_NOMINMAX, nullptr
     },
     {
diff --git a/kml.cc b/kml.cc
index 9d529e108e48daca0f9d2fa7e3567f9c435ff987..cce73048c41d8ea6b96fedc2e34e4f6c16ed3943 100644 (file)
--- a/kml.cc
+++ b/kml.cc
 
 #include "kml.h"
 
-#include <cctype>                      // for tolower, toupper
 #include <cmath>                       // for fabs
 #include <cstdio>                      // for sscanf, printf
-#include <cstdlib>                     // for strtod
-#include <cstring>                     // for strcmp
 #include <optional>                    // for optional
 #include <tuple>                       // for tuple, make_tuple
 
@@ -105,8 +102,8 @@ const QVector<KmlFormat::mt_field_t> KmlFormat::mt_fields_def = {
 
 void KmlFormat::kml_init_color_sequencer(int steps_per_rev)
 {
-  if (rotate_colors) {
-    float color_step = strtod(opt_rotate_colors, nullptr);
+  if (opt_rotate_colors) {
+    float color_step = opt_rotate_colors.get_result();
     if (color_step > 0.0f) {
       // step around circle by given number of degrees for each track(route)
       kml_color_sequencer.step = static_cast<float>(kml_color_limit) * 6.0f * color_step / 360.0f;
@@ -389,31 +386,28 @@ void KmlFormat::rd_deinit()
 
 void KmlFormat::wr_init(const QString& fname)
 {
-  char u = 's';
   waypt_init_bounds(&kml_bounds);
   kml_time_min = gpsbabel::DateTime();
   kml_time_max = gpsbabel::DateTime();
 
-  if (opt_units) {
-    u = tolower(opt_units[0]);
-  }
+  QChar u = opt_units.isEmpty()? 's' : opt_units.get().front().toLower();
 
   unitsformatter = new UnitsFormatter();
-  switch (u) {
-  case 's':
+  switch (u.unicode()) {
+  case u's':
     unitsformatter->setunits(UnitsFormatter::units_t::statute);
     break;
-  case 'm':
+  case u'm':
     unitsformatter->setunits(UnitsFormatter::units_t::metric);
     break;
-  case 'n':
+  case u'n':
     unitsformatter->setunits(UnitsFormatter::units_t::nautical);
     break;
-  case 'a':
+  case u'a':
     unitsformatter->setunits(UnitsFormatter::units_t::aviation);
     break;
   default:
-    fatal("Units argument '%s' should be 's' for statute units, 'm' for metric, 'n' for nautical or 'a' for aviation.\n", qPrintable(opt_units.get()));
+    fatal("Units argument '%s' should be 's' for statute units, 'm' for metric, 'n' for nautical or 'a' for aviation.\n", qPrintable(opt_units));
     break;
   }
   /*
@@ -435,7 +429,7 @@ void KmlFormat::wr_position_init(const QString& fname)
   posnfilename = fname;
   posnfilenametmp = QStringLiteral("%1-").arg(fname);
   realtime_positioning = true;
-  max_position_points = xstrtoi(opt_max_position_points, nullptr, 10);
+  max_position_points = opt_max_position_points.get_result();
 }
 
 void KmlFormat::wr_deinit()
@@ -516,7 +510,7 @@ void KmlFormat::kml_write_bitmap_style_(const QString& style, const QString& bit
   }
 
   if (is_multitrack) {
-    kml_output_linestyle(opt_line_color.get(),
+    kml_output_linestyle(opt_line_color,
                          highlighted ? line_width + 2 :
                          line_width);
   }
@@ -885,7 +879,7 @@ void KmlFormat::kml_output_point(const Waypoint* waypointp, kml_point_type pt_ty
       writer->writeStartElement(QStringLiteral("Style"));
       writer->writeStartElement(QStringLiteral("IconStyle"));
       writer->writeStartElement(QStringLiteral("Icon"));
-      writer->writeTextElement(QStringLiteral("href"), opt_deficon.get());
+      writer->writeTextElement(QStringLiteral("href"), opt_deficon);
       writer->writeEndElement(); // Close Icon tag
       writer->writeEndElement(); // Close IconStyle tag
       writer->writeEndElement(); // Close Style tag
@@ -934,17 +928,17 @@ void KmlFormat::kml_output_tailer(const route_head* header)
     }
     writer->writeStartElement(QStringLiteral("Placemark"));
     writer->writeTextElement(QStringLiteral("name"), QStringLiteral("Path"));
-    if (!rotate_colors) {
+    if (!opt_rotate_colors) {
       writer->writeTextElement(QStringLiteral("styleUrl"), QStringLiteral("#lineStyle"));
     }
-    if (header->line_color.bbggrr >= 0 || header->line_width >= 0 || rotate_colors) {
+    if (header->line_color.bbggrr >= 0 || header->line_width >= 0 || opt_rotate_colors) {
       writer->writeStartElement(QStringLiteral("Style"));
       writer->writeStartElement(QStringLiteral("LineStyle"));
-      if (rotate_colors) {
+      if (opt_rotate_colors) {
         kml_step_color();
         writer->writeTextElement(QStringLiteral("color"), QStringLiteral("%1%2")
                                  .arg(kml_color_sequencer.color.opacity, 2, 16, QChar('0')).arg(kml_color_sequencer.color.bbggrr, 6, 16, QChar('0')));
-        writer->writeTextElement(QStringLiteral("width"), opt_line_width.get());
+        writer->writeTextElement(QStringLiteral("width"), opt_line_width);
       } else {
         if (header->line_color.bbggrr >= 0) {
           writer->writeTextElement(QStringLiteral("color"), QStringLiteral("%1%2")
@@ -1455,7 +1449,7 @@ void KmlFormat::kml_waypt_pr(const Waypoint* waypointp) const
   kml_output_timestamp(waypointp);
 
   // Icon - but only if it looks like a URL.
-  icon = opt_deficon ? opt_deficon.get() : waypointp->icon_descr;
+  icon = opt_deficon ? opt_deficon : waypointp->icon_descr;
   if (icon.contains("://")) {
     writer->writeStartElement(QStringLiteral("Style"));
     writer->writeStartElement(QStringLiteral("IconStyle"));
@@ -1849,11 +1843,10 @@ void KmlFormat::write()
   export_track = opt_export_track;
   floating = opt_floating;
   extrude = opt_extrude;
-  rotate_colors = (!! opt_rotate_colors);
   trackdata = opt_trackdata;
   trackdirection = opt_trackdirection;
-  line_width = xstrtoi(opt_line_width, nullptr, 10);
-  precision = xstrtoi(opt_precision, nullptr, 10);
+  line_width = opt_line_width.get_result();
+  precision = opt_precision.get_result();
 
   writer->writeStartDocument();
 
@@ -1902,7 +1895,7 @@ void KmlFormat::write()
   if (track_waypt_count() || route_waypt_count()) {
     writer->writeStartElement(QStringLiteral("Style"));
     writer->writeAttribute(QStringLiteral("id"), QStringLiteral("lineStyle"));
-    kml_output_linestyle(opt_line_color.get(), line_width);
+    kml_output_linestyle(opt_line_color, line_width);
     writer->writeEndElement(); // Close Style tag
   }
 
diff --git a/kml.h b/kml.h
index 2454101d6cf0381768203dd8e9d8b7115463d1f1..7ebc1200976e0f4a6da8e4f07761029e0d85ce51 100644 (file)
--- a/kml.h
+++ b/kml.h
@@ -34,7 +34,7 @@
 
 #include "defs.h"
 #include "format.h"
-#include "option.h"                    // for OptionBool, OptionCString
+#include "option.h"                    // for OptionBool, OptionString
 #include "src/core/datetime.h"         // for DateTime
 #include "src/core/file.h"             // for File
 #include "src/core/xmlstreamwriter.h"  // for XmlStreamWriter
@@ -194,21 +194,21 @@ private:
   QHash<const route_head*, track_trait_t> kml_track_traits_hash;
 
   // options
-  OptionCString opt_deficon;
+  OptionString opt_deficon;
   OptionBool opt_export_lines;
   OptionBool opt_export_points;
   OptionBool opt_export_track;
-  OptionCString opt_line_width;
-  OptionCString opt_line_color;
+  OptionInt opt_line_width;
+  OptionString opt_line_color;
   OptionBool opt_floating;
   OptionBool opt_extrude;
   OptionBool opt_trackdata;
   OptionBool opt_trackdirection;
-  OptionCString opt_units;
+  OptionString opt_units;
   OptionBool opt_labels;
-  OptionCString opt_max_position_points;
-  OptionCString opt_rotate_colors;
-  OptionCString opt_precision;
+  OptionInt opt_max_position_points;
+  OptionDouble opt_rotate_colors;
+  OptionInt opt_precision;
 
   bool export_lines{};
   bool export_points{};
@@ -218,7 +218,6 @@ private:
   bool trackdata{};
   bool trackdirection{};
   int max_position_points{};
-  bool rotate_colors{};
   int line_width{};
   int precision{};
 
index 25248d49a83f2303a3c78cba6929a8ecebd696fe..cdf1b1e271a9d3fc96707b97298124c4751fa744 100644 (file)
@@ -92,7 +92,6 @@
 #include <cstdio>               // for printf, sprintf, SEEK_CUR
 #include <cstdint>              // for int64_t
 #include <cstdlib>              // for abs
-#include <cstring>              // for strcmp, strlen
 #include <numbers>              // for pi
 #include <utility>              // for as_const
 
@@ -347,9 +346,9 @@ LowranceusrFormat::wr_init(const QString& fname)
   file_out = gbfopen_le(fname, "wb", MYNAME);
   mkshort_handle = new MakeShort;
   waypt_out_count = 0;
-  writing_version = xstrtoi(opt_wversion, nullptr, 10);
+  writing_version = opt_wversion.get_result();
   if ((writing_version < 2) || (writing_version > 4)) {
-    fatal(MYNAME " wversion value %s is not supported !!\n", qPrintable(opt_wversion.get()));
+    fatal(MYNAME " wversion value %s is not supported !!\n", qPrintable(opt_wversion));
   }
   utf16le_codec = QTextCodec::codecForName("UTF-16LE");
   waypt_table = new QList<const Waypoint*>;
@@ -1884,7 +1883,7 @@ LowranceusrFormat::write()
 
     /* file title */
     buf = opt_title.isEmpty()?
-          QStringLiteral("GPSBabel generated USR data file") : opt_title.get();
+          QStringLiteral("GPSBabel generated USR data file") : opt_title;
     if (global_opts.debug_level >= 1) {
       printf(MYNAME " data_write: Title = '%s'\n", qPrintable(buf));
     }
@@ -1903,12 +1902,12 @@ LowranceusrFormat::write()
     gbfputc(0, file_out);
 
     /* device serial number */
-    opt_serialnum_i = xstrtoi(opt_serialnum, nullptr, 10);
+    opt_serialnum_i = opt_serialnum.get_result();
     gbfputint32(opt_serialnum_i, file_out);
 
     /* content description */
     buf = opt_content_descr.isEmpty()?
-          QStringLiteral("Waypoints, routes, and trails") : opt_content_descr.get();
+          QStringLiteral("Waypoints, routes, and trails") : opt_content_descr;
     if (global_opts.debug_level >= 1) {
       printf(MYNAME " data_write: Description = '%s'\n", qPrintable(buf));
     }
index ae990f9061612b9527cda2ca6cf489e53cdbe2b3..1ac7bd8cf4e052edaa76ec191093d587cae45641 100644 (file)
 #include "formspec.h"           // for FsChainFind, FsChainAdd, kFsLowranceusr4, FormatSpecificData
 #include "gbfile.h"             // for gbfgetint32, gbfputint32, gbfputint16, gbfgetc, gbfgetint16, gbfwrite, gbfputc, gbfeof, gbfgetflt, gbfclose, gbfgetdbl, gbfopen_le, gbfputdbl, gbfputs, gbfile, gbfputflt, gbfread, gbfseek
 #include "mkshort.h"            // for MakeShort
-#include "option.h"             // for OptionBool, OptionCString
+#include "option.h"             // for OptionBool, OptionString
 #include "src/core/datetime.h"  // for DateTime
 
 
@@ -448,10 +448,10 @@ private:
   OptionBool  opt_ignoreicons;
   OptionBool  opt_writeasicons;
   OptionBool  opt_seg_break;
-  OptionCString  opt_wversion;
-  OptionCString  opt_title;
-  OptionCString  opt_content_descr;
-  OptionCString  opt_serialnum;
+  OptionInt  opt_wversion;
+  OptionString  opt_title;
+  OptionString  opt_content_descr;
+  OptionInt  opt_serialnum;
   int            opt_serialnum_i{};
 
   QList<const Waypoint*>* waypt_table{nullptr};
index fa9e3e7f9c43b43077eeea596b484376a9d49f40..31afa0d54d4b69513af5f79e50c59039c9e899f8 100644 (file)
@@ -54,6 +54,7 @@
 
 #include "mtk_logger.h"
 
+#include <algorithm>            // for clamp
 #include <cctype>               // for isdigit
 #include <cstdarg>              // for va_end, va_start
 #include <cstring>              // for memcmp, memset, strncmp, strlen, memmove, strchr, strcpy, strerror, strstr
@@ -72,9 +73,9 @@
 #include <QStringLiteral>       // for qMakeStringPrivate, QStringLiteral
 #include <QThread>              // for QThread
 #include <QtCore>               // for qPrintable, UTC
-#include <cerrno>               // for errno, ERANGE
+#include <cerrno>               // for errno
 #include <cmath>                // for fabs
-#include <cstdlib>              // for strtoul, strtol
+#include <cstdlib>              // for strtoul
 
 #include "defs.h"
 #include "gbfile.h"             // for gbfprintf, gbfputc, gbfputs, gbfclose, gbfopen, gbfile
@@ -427,12 +428,7 @@ void MtkLoggerBase::mtk_read()
 
   unsigned int scan_step = 0x10000;
   unsigned int scan_bsize = 0x0400;
-  unsigned int read_bsize_kb = strtol(OPT_block_size_kb, nullptr, 10);
-  if (errno == ERANGE || read_bsize_kb < 1) {
-    read_bsize_kb = 1;
-  } else if (read_bsize_kb > 64) {
-    read_bsize_kb = 64;
-  }
+  unsigned int read_bsize_kb = std::clamp(OPT_block_size_kb.get_result(), 1, 64);
   unsigned int read_bsize = read_bsize_kb * 1024;
   dbg(2, "Download block size is %d bytes\n", read_bsize);
   if (init_scan) {
@@ -733,21 +729,21 @@ int MtkLoggerBase::add_trackpoint(int idx, unsigned long bmask, data_item* itm)
 
 
 /********************** MTK Logger -- CSV output *************************/
-void MtkLoggerBase::mtk_csv_init(const char* csv_fname, unsigned long bitmask)
+void MtkLoggerBase::mtk_csv_init(const QString& csv_fname, unsigned long bitmask)
 {
   FILE* cf;
 
-  dbg(1, "Opening csv output file %s...\n", csv_fname);
+  dbg(1, "Opening csv output file %s...\n", qPrintable(csv_fname));
 
   // can't use gbfopen here - it will fatal() if file doesn't exist
-  if ((cf = ufopen(QString::fromUtf8(csv_fname), "r")) != nullptr) {
+  if ((cf = ufopen(csv_fname, "r")) != nullptr) {
     fclose(cf);
-    warning(MYNAME ": CSV file %s already exist ! Cowardly refusing to overwrite.\n", csv_fname);
+    warning(MYNAME ": CSV file %s already exist ! Cowardly refusing to overwrite.\n", qPrintable(csv_fname));
     return;
   }
 
   if ((cd = gbfopen(csv_fname, "w", MYNAME)) == nullptr) {
-    fatal(MYNAME ": Can't open csv file '%s'\n", csv_fname);
+    fatal(MYNAME ": Can't open csv file '%s'\n", qPrintable(csv_fname));
   }
 
   /* Add the header line */
@@ -1396,8 +1392,8 @@ void MtkLoggerBase::file_read()
 
   mtk_info.logLen = mtk_log_len(mtk_info.bitmask);
   dbg(3, "Log item size %d bytes\n", mtk_info.logLen);
-  if (csv_file && *csv_file) {
-    mtk_csv_init(static_cast<const char*>(csv_file), mtk_info.bitmask);
+  if (!csv_file.isEmpty()) {
+    mtk_csv_init(csv_file, mtk_info.bitmask);
   }
 
   while (pos < fsize && (bLen = fread(&buf[j], 1, sizeof(buf)-j, fl)) > 0) {
index 1059943c2943f0c3e10682a6daa62baa3b566bcf..662e4606e0727a0e8cf80c8f19bfdec842c91f52 100644 (file)
@@ -63,7 +63,7 @@
 #include "defs.h"
 #include "format.h"  // for Format
 #include "gbfile.h"  // for gbfile
-#include "option.h"  // for OptionBool, OptionCString
+#include "option.h"  // for OptionBool, OptionString
 
 
 class MtkLoggerBase
@@ -220,8 +220,8 @@ protected:
   OptionBool OPT_erase;  /* erase ? command option */
   OptionBool OPT_erase_only;  /* erase_only ? command option */
   OptionBool OPT_log_enable;  /* enable ? command option */
-  OptionCString csv_file; /* csv ? command option */
-  OptionCString OPT_block_size_kb; /* block_size_kb ? command option */
+  OptionString csv_file; /* csv ? command option */
+  OptionInt OPT_block_size_kb; /* block_size_kb ? command option */
   MTK_DEVICE_TYPE mtk_device = MTK_LOGGER;
 
   mtk_loginfo mtk_info{};
@@ -280,7 +280,7 @@ protected:
   int mtk_erase();
   void mtk_read();
   int add_trackpoint(int idx, long unsigned int bmask, data_item* itm);
-  void mtk_csv_init(const char* csv_fname, long unsigned int bitmask);
+  void mtk_csv_init(const QString& csv_fname, long unsigned int bitmask);
   void mtk_csv_deinit();
   static int csv_line(gbfile* csvFile, int idx, long unsigned int bmask, data_item* itm);
   int mtk_parse(unsigned char* data, int dataLen, unsigned int bmask);
diff --git a/nmea.cc b/nmea.cc
index 91d1899c902206b6044587e4556fd9fc14f0f8e9..d81d358b5daa2d8b06636e79f4f191ab7a29e7f0 100644 (file)
--- a/nmea.cc
+++ b/nmea.cc
@@ -23,7 +23,6 @@
 #include <cctype>                  // for isprint
 #include <cmath>                   // for fabs
 #include <cstdio>                  // for snprintf, sscanf, fprintf, fputc, stderr
-#include <cstdlib>                 // for strtod
 #include <cstring>                 // for strncmp, strchr, strlen, strstr, memset, strrchr
 #include <iterator>                // for operator!=, reverse_iterator
 
@@ -280,15 +279,15 @@ NmeaFormat::wr_init(const QString& fname)
 
   sleepms = -1;
   if (opt_sleep) {
-    if (*opt_sleep) {
-      sleepms = 1e3 * strtod(opt_sleep, nullptr);
+    if (!opt_sleep.isEmpty()) {
+      sleepms = 1e3 * opt_sleep.get_result();
     } else {
       sleepms = -1;
     }
   }
 
   mkshort_handle = new MakeShort;
-  mkshort_handle->set_length(xstrtoi(snlenopt, nullptr, 10));
+  mkshort_handle->set_length(snlenopt.get_result());
 
   if (opt_gisteq) {
     opt_gpgga.reset();
@@ -816,7 +815,7 @@ NmeaFormat::nmea_fix_timestamps(route_head* track)
   }
 
   if (!prev_datetime.date().isValid()) {
-    if (optdate == nullptr) {
+    if (!optdate) {
       warning(MYNAME ": No date found within track (all points dropped)!\n");
       warning(MYNAME ": Please use option \"date\" to preset a valid date for those tracks.\n");
       track_del_head(track);
@@ -981,9 +980,9 @@ NmeaFormat::read()
   }
 
   if (optdate) {
-    opt_tm = QDate::fromString(optdate.get(), u"yyyyMMdd");
+    opt_tm = QDate::fromString(optdate, u"yyyyMMdd");
     if (!opt_tm.isValid()) {
-      fatal(MYNAME ": Invalid date \"%s\"!\n", qPrintable(optdate.get()));
+      fatal(MYNAME ": Invalid date \"%s\"!\n", qPrintable(optdate));
     }
   }
 
@@ -1045,8 +1044,8 @@ NmeaFormat::rd_position_init(const QString& fname)
   gbser_flush(gbser_handle);
 
   if (opt_baud) {
-    if (!gbser_set_speed(gbser_handle, xstrtoi(opt_baud, nullptr, 10))) {
-      fatal(MYNAME ": Unable to set baud rate %s\n", qPrintable(opt_baud.get()));
+    if (!gbser_set_speed(gbser_handle, opt_baud.get_result())) {
+      fatal(MYNAME ": Unable to set baud rate %s\n", qPrintable(opt_baud));
     }
   }
   posn_fname = fname;
diff --git a/nmea.h b/nmea.h
index 9f5ca4f4c3d176e3e6a63b8162227cf9f0b0edc9..495360d89711f8e84908b2ffc58e72e1a02baaf8 100644 (file)
--- a/nmea.h
+++ b/nmea.h
@@ -34,7 +34,7 @@
 #include "format.h"    // for Format
 #include "gbfile.h"    // for gbfile
 #include "mkshort.h"   // for MakeShort
-#include "option.h"    // for OptionBool, OptionCString
+#include "option.h"    // for OptionBool, OptionString
 
 
 class NmeaFormat : public Format
@@ -140,16 +140,16 @@ private:
   OptionBool opt_gpgga;
   OptionBool opt_gpvtg;
   OptionBool opt_gpgsa;
-  OptionCString snlenopt;
-  OptionCString optdate;
+  OptionInt snlenopt;
+  OptionInt optdate;
   OptionBool getposnarg;
-  OptionCString opt_sleep;
-  OptionCString opt_baud;
+  OptionInt opt_sleep;
+  OptionInt opt_baud;
   OptionBool opt_append;
   OptionBool opt_gisteq;
   OptionBool opt_ignorefix;
 
-  long sleepms{};
+  int sleepms{};
   int getposn{};
   bool amod_waypoint{};
 
diff --git a/option.cc b/option.cc
new file mode 100644 (file)
index 0000000..5f9c887
--- /dev/null
+++ b/option.cc
@@ -0,0 +1,54 @@
+/*
+    Copyright (C) 2024 Robert Lipe, robertlipe+source@gpsbabel.org
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+ */
+#include "option.h"
+
+#include <QString>             // for QString
+
+#include "defs.h"              // for parse_double, parse_integer
+
+
+int OptionString::toInt() const
+{
+  return parse_integer(value_, id_);
+}
+
+int OptionString::toInt(bool* ok) const
+{
+  return parse_integer(value_, id_, ok);
+}
+
+int OptionString::toInt(bool* ok, QString* end, int base) const
+{
+  return parse_integer(value_, id_, ok, end, base);
+}
+
+double OptionString::toDouble() const
+{
+  return parse_double(value_, id_);
+}
+
+double OptionString::toDouble(bool* ok) const
+{
+  return parse_double(value_, id_, ok);
+}
+
+double OptionString::toDouble(bool* ok, QString* end) const
+{
+  return parse_double(value_, id_, ok, end);
+}
index 12fb46bbd97a73b18837a67dc58643c01b1153c3..db4051a52f4f7b214b77f4d8c8d5ec558cf97a4a 100644 (file)
--- a/option.h
+++ b/option.h
 class Option /* Abstract Class */
 {
 public:
-  /* Types */
-  enum option_t {
-    type_cstring,
-    type_boolean,
-  };
-
   /* Special Member Functions */
   Option() = default;
   // Provide virtual public destructor to avoid undefined behavior when
@@ -47,19 +41,21 @@ public:
   Option& operator=(Option&&) = delete;
 
   /* Member Functions */
-  [[nodiscard]] virtual option_t type() const = 0;
   [[nodiscard]] virtual bool has_value() const = 0;
   virtual void reset() = 0;
   [[nodiscard]] virtual bool isEmpty() const = 0;
   [[nodiscard]] virtual const QString& get() const = 0;
   virtual void set(const QString& s) = 0;
+  virtual void set_id(const QString& id)
+  {
+  }
 
   /* Data Members */
   // I.25: Prefer empty abstract classes as interfaces to class hierarchies
   // https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#i25-prefer-empty-abstract-classes-as-interfaces-to-class-hierarchies
 };
 
-class OptionCString : public Option
+class [[deprecated]] OptionCString : public Option
 {
 public:
   /* Special Member Functions */
@@ -76,11 +72,6 @@ public:
     return value_.isNull()? nullptr : valueb_.constData();
   }
 
-  [[nodiscard]] option_t type() const override
-  {
-    return type_cstring;
-  }
-
   [[nodiscard]] bool has_value() const override
   {
     return !value_.isNull();
@@ -118,23 +109,213 @@ private:
   QByteArray valueb_;
 };
 
+class OptionString : public Option
+{
+public:
+  /* Special Member Functions */
+  OptionString() = default;
+
+  explicit(false) operator const QString& () const
+  {
+    return value_;
+  }
+
+  explicit(false) operator bool () const
+  {
+    return !value_.isNull();
+  }
+
+  [[nodiscard]] bool has_value() const override
+  {
+    return !value_.isNull();
+  }
+
+  void reset() override
+  {
+    value_ = QString();
+  }
+
+  [[nodiscard]] bool isEmpty() const override
+  {
+    return value_.isEmpty();
+  }
+
+  [[nodiscard]] const QString& get() const override
+  {
+    return value_;
+  }
+
+  void set(const QString& s) override
+  {
+    value_ = s;
+  }
+
+  void set_id(const QString& id) override
+  {
+    id_ = id;
+  }
+
+// We use overloads instead of default parameters to enable tool visibility into different usages.
+  int toInt() const;
+  int toInt(bool* ok) const;
+  int toInt(bool* ok, QString* end, int base) const;
+  double toDouble() const;
+  double toDouble(bool* ok) const;
+  double toDouble(bool* ok, QString* end) const;
+
+private:
+  QString value_;
+  QString id_;
+};
+
+class OptionInt : public Option
+{
+public:
+  /* Special Member Functions */
+  OptionInt() = default;
+
+  explicit(false) operator const QString& () const
+  {
+    return value_;
+  }
+
+  explicit(false) operator bool () const
+  {
+    return !value_.isNull();
+  }
+
+  [[nodiscard]] bool has_value() const override
+  {
+    return !value_.isNull();
+  }
+
+  void reset() override
+  {
+    value_ = QString();
+    result_ = 0;
+    end_ = QString();
+  }
+
+  [[nodiscard]] bool isEmpty() const override
+  {
+    return value_.isEmpty();
+  }
+
+  [[nodiscard]] const QString& get() const override
+  {
+    return value_;
+  }
+
+  void set(const QString& s) override
+  {
+    value_ = s;
+  }
+
+  void set_id(const QString& id) override
+  {
+    id_ = id;
+  }
+
+  void set_result(int result, const QString& end)
+  {
+    result_ = result;
+    end_ = end;
+  }
+
+  int get_result(QString* end = nullptr) const {
+    if (end != nullptr) {
+      *end = end_;
+    }
+    return result_;
+  }
+
+private:
+  QString value_;
+  QString id_;
+  int result_{};
+  QString end_;
+};
+
+class OptionDouble : public Option
+{
+public:
+  /* Special Member Functions */
+  OptionDouble() = default;
+
+  explicit(false) operator const QString& () const
+  {
+    return value_;
+  }
+
+  explicit(false) operator bool () const
+  {
+    return !value_.isNull();
+  }
+
+  [[nodiscard]] bool has_value() const override
+  {
+    return !value_.isNull();
+  }
+
+  void reset() override
+  {
+    value_ = QString();
+    result_ = 0.0;
+    end_ = QString();
+  }
+
+  [[nodiscard]] bool isEmpty() const override
+  {
+    return value_.isEmpty();
+  }
+
+  [[nodiscard]] const QString& get() const override
+  {
+    return value_;
+  }
+
+  void set(const QString& s) override
+  {
+    value_ = s;
+  }
+
+  void set_id(const QString& id) override
+  {
+    id_ = id;
+  }
+
+  void set_result(double result, const QString& end)
+  {
+    result_ = result;
+    end_ = end;
+  }
+
+  double get_result(QString* end = nullptr) const {
+    if (end != nullptr) {
+      *end = end_;
+    }
+    return result_;
+  }
+
+private:
+  QString value_;
+  QString id_;
+  double result_{};
+  QString end_;
+};
+
 class OptionBool : public Option
 {
 public:
   /* Special Member Functions */
   OptionBool() = default;
 
-  /* Traditionally unsupplied bool options without default are considered to be false. */
+  /* Traditionally un-supplied bool options without default are considered to be false. */
   explicit(false) operator bool() const
   {
     return (!value_.isNull() && (value_ != '0'));
   }
 
-  [[nodiscard]] option_t type() const override
-  {
-    return type_boolean;
-  }
-
   /* Note that has_value can be used to distinguish an option that wasn't supplied
    * from one that was supplied and is considered false by Vecs::assign_option.
    */
diff --git a/osm.cc b/osm.cc
index 230da84103d83e3644669ccbf192de9366dc5f16..06670f403d32fc0c1ad92496ec7f3bc2f01d68e0 100644 (file)
--- a/osm.cc
+++ b/osm.cc
@@ -754,7 +754,7 @@ OsmFormat::osm_waypt_disp(const Waypoint* waypoint)
     break;
   }
 
-  if (QString creator = created_by.get(); !creator.isEmpty()) {
+  if (QString creator = created_by; !creator.isEmpty()) {
     if (!gpsbabel_testmode()) {
       if (creator == "GPSBabel") {
         creator += '-';
@@ -770,7 +770,7 @@ OsmFormat::osm_waypt_disp(const Waypoint* waypoint)
     osm_disp_feature(waypoint);
   }
 
-  osm_write_opt_tag(opt_tagnd.get());
+  osm_write_opt_tag(opt_tagnd);
 
   fout->writeEndElement(); // node
 }
@@ -813,7 +813,7 @@ OsmFormat::osm_rte_disp_trail(const route_head* route)
     return;
   }
 
-  if (QString creator = created_by.get(); !creator.isEmpty()) {
+  if (QString creator = created_by; !creator.isEmpty()) {
     if (!gpsbabel_testmode()) {
       if (creator == "GPSBabel") {
         creator += '-';
@@ -826,7 +826,7 @@ OsmFormat::osm_rte_disp_trail(const route_head* route)
   osm_write_tag("name", route->rte_name);
   osm_write_tag("note", route->rte_desc);
 
-  osm_write_opt_tag(opt_tag.get());
+  osm_write_opt_tag(opt_tag);
 
   fout->writeEndElement(); // way
 }
diff --git a/osm.h b/osm.h
index b983e2dacfaf7d07ed9eade397aa4ae710765149..709d7a88f0127252f7b1f0045d3a8d895f811d66 100644 (file)
--- a/osm.h
+++ b/osm.h
@@ -32,7 +32,7 @@
 
 #include "defs.h"
 #include "format.h"                    // for Format
-#include "option.h"                    // for OptionCString
+#include "option.h"                    // for OptionString
 #include "src/core/file.h"             // for File
 #include "src/core/xmlstreamwriter.h"  // for XmlStreamWriter
 #include "xmlgeneric.h"                // for xg_functor_map_entry, cb_start, cb_end
@@ -111,9 +111,9 @@ private:
 
   /* Data Members */
 
-  OptionCString opt_tag;
-  OptionCString opt_tagnd;
-  OptionCString created_by;
+  OptionString opt_tag;
+  OptionString opt_tagnd;
+  OptionString created_by;
 
   QVector<arglist_t> osm_args = {
     { "tag", &opt_tag,         "Write additional way tag key/value pairs", nullptr, ARGTYPE_STRING, ARG_NOMINMAX, nullptr},
diff --git a/ozi.cc b/ozi.cc
index 283c5f4cda8652f64aea8ff5fc63d57124673914..f0c7e8d91b7254e560880584fda4f86d377257f5 100644 (file)
--- a/ozi.cc
+++ b/ozi.cc
@@ -38,7 +38,6 @@
 
 #include "ozi.h"
 
-#include <cctype>                 // for tolower
 #include <cmath>                  // for lround
 
 #include <QByteArray>             // for QByteArray
@@ -72,7 +71,7 @@ void
 OziFormat::ozi_open_io(const QString& fname, QIODevice::OpenModeFlag mode)
 {
   stream = new gpsbabel::TextStream;
-  stream->open(fname, mode, MYNAME, opt_codec);
+  stream->open(fname, mode, MYNAME, opt_codec.get().toUtf8());
 
   if (mode & QFile::WriteOnly) {
     stream->setRealNumberNotation(QTextStream::FixedNotation);
@@ -96,8 +95,8 @@ OziFormat::ozi_alloc_fsdata()
   auto* fsdata = new ozi_fsdata;
 
   /* Provide defaults via command line defaults */
-  fsdata->fgcolor = color_to_bbggrr(wptfgcolor.get());
-  fsdata->bgcolor = color_to_bbggrr(wptbgcolor.get());
+  fsdata->fgcolor = color_to_bbggrr(wptfgcolor);
+  fsdata->bgcolor = color_to_bbggrr(wptbgcolor);
 
   return fsdata;
 }
@@ -323,37 +322,33 @@ OziFormat::ozi_route_pr()
 void
 OziFormat::ozi_init_units(const int direction) /* 0 = in; 1 = out */
 {
-  altunit = tolower(*altunit_opt);
-  switch (altunit) {
-  case 'm': /* meters, okay */
-    alt_scale = 1.0;
-    break;
-  case 'f': /* feet, okay */
-    alt_scale = FEET_TO_METERS(1.0);
-    break;
-  default:
-    fatal(MYNAME ": Unknown value (%s) for option 'altunit'!\n", qPrintable(altunit_opt.get()));
+  if (altunit_opt.get().startsWith('m', Qt::CaseInsensitive)) {
+    altunit = 'm';
+    alt_scale = 1.0; /* meters */
+  } else if (altunit_opt.get().startsWith('f', Qt::CaseInsensitive)) {
+    altunit = 'f';
+    alt_scale = FEET_TO_METERS(1.0); /* feet */
+  } else {
+    fatal(MYNAME ": Unknown value (%s) for option 'altunit'!\n", qPrintable(altunit_opt));
   }
   if (direction != 0) {
-    alt_scale = 1 / alt_scale;
-  }
-
-  proxunit = tolower(*proxunit_opt);
-  switch (proxunit) {
-  case 'm': /* miles, okay */
-    prox_scale = MILES_TO_METERS(1.0);
-    break;
-  case 'n': /* nautical miles, okay */
-    prox_scale = NMILES_TO_METERS(1.0);
-    break;
-  case 'k': /* kilometers, okay */
-    prox_scale = 1000.0;
-    break;
-  default:
-    fatal(MYNAME ": Unknown value (%s) for option 'proxunit'!\n", qPrintable(proxunit_opt.get()));
+    alt_scale = 1.0 / alt_scale;
+  }
+
+  if (proxunit_opt.get().startsWith('m')) {
+    proxunit = 'm';
+    prox_scale = MILES_TO_METERS(1.0); /* miles */
+  } else if (proxunit_opt.get().startsWith('n')) {
+    proxunit = 'n';
+    prox_scale = NMILES_TO_METERS(1.0); /* nautical miles */
+  } else if (proxunit_opt.get().startsWith('k')) {
+    proxunit = 'k';
+    prox_scale = 1000.0; /* kilometers */
+  } else {
+    fatal(MYNAME ": Unknown value (%s) for option 'proxunit'!\n", qPrintable(proxunit_opt));
   }
   if (direction != 0) {
-    prox_scale = 1 / prox_scale;
+    prox_scale = 1.0 / prox_scale;
   }
 }
 
@@ -387,7 +382,7 @@ OziFormat::wr_init(const QString& fname)
   /* set mkshort options from the command line if applicable */
   if (global_opts.synthesize_shortnames) {
 
-    mkshort_handle->set_length(xstrtoi(snlenopt, nullptr, 10));
+    mkshort_handle->set_length(snlenopt.get_result());
 
     if (snwhiteopt.has_value()) {
       mkshort_handle->set_whitespace_ok(snwhiteopt);
@@ -405,7 +400,7 @@ OziFormat::wr_init(const QString& fname)
   }
 
   ozi_init_units(1);
-  parse_distance(proximityarg, &proximity, 1 / prox_scale, MYNAME);
+  parse_distance(proximityarg, &proximity, 1.0 / prox_scale, MYNAME);
 }
 
 void
diff --git a/ozi.h b/ozi.h
index 78cf9a606ea364af14030bc8689391899d328d48..4f20a224519f2976fc28806e3836b54a9fa9a7e3 100644 (file)
--- a/ozi.h
+++ b/ozi.h
@@ -47,7 +47,7 @@
 #include "format.h"               // for Format
 #include "formspec.h"             // for FormatSpecificData, kFsOzi
 #include "mkshort.h"              // for MakeShort
-#include "option.h"               // for OptionCString, OptionBool
+#include "option.h"               // for OptionString, OptionBool
 #include "src/core/textstream.h"  // for TextStream
 
 
@@ -126,23 +126,23 @@ private:
   int route_wpt_count{};
   int new_track{};
 
-  OptionCString snlenopt;
+  OptionInt snlenopt;
   OptionBool snwhiteopt;
   OptionBool snupperopt;
   OptionBool snuniqueopt;
-  OptionCString wptfgcolor;
-  OptionCString wptbgcolor;
+  OptionString wptfgcolor;
+  OptionString wptbgcolor;
   OptionBool pack_opt;
   int datum{};
-  OptionCString proximityarg;
+  OptionDouble proximityarg;
   double proximity{};
-  OptionCString altunit_opt;
-  OptionCString proxunit_opt;
+  OptionString altunit_opt;
+  OptionString proxunit_opt;
   char altunit{};
   char proxunit{};
   double alt_scale{};
   double prox_scale{};
-  OptionCString opt_codec;
+  OptionString opt_codec;
 
   QVector<arglist_t> ozi_args = {
     {
@@ -175,7 +175,7 @@ private:
     },
     {
       "proximity", &proximityarg, "Proximity distance",
-      "0", ARGTYPE_STRING, ARG_NOMINMAX, nullptr
+      "0",  ARGTYPE_ALLOW_TRAILING_DATA | ARGTYPE_STRING, ARG_NOMINMAX, nullptr
     },
     {
       "altunit", &altunit_opt, "Unit used in altitude values",
index b14c27227ed09c55d86b07da85ed9ad9db80331c..2ea5bb63da2bb42fc537d08c30a329953a244865 100644 (file)
--- a/parse.cc
+++ b/parse.cc
 
  */
 
-#include <cctype>           // for isspace
-#include <cmath>            // for fabs
-#include <cstdio>           // for sscanf
-#include <cstdlib>          // for strtod
+#include <cmath>                   // for fabs
+#include <cstdio>                  // for sscanf
+#include <stdexcept>               // for invalid_argument, out_of_range
+#include <string>                  // for stod
 
-#include <QString>          // for QString
+#include <QString>                 // for QString
+#include <QtGlobal>                // for qPrintable
 
-#include "defs.h"           // for case_ignore_strcmp, fatal, KPH_TO_MPS, MPH_TO_MPS, warning, FEET_TO_METERS, KNOTS_TO_MPS, CSTR, FATHOMS_TO_METERS, MILES_TO_METERS, NMILES_TO_METERS, kDatumWGS84, grid_type, parse_coordinates, parse_distance, parse_speed, grid_bng, grid_lat_lon_ddd, grid_lat_lo...
-#include "jeeps/gpsmath.h"  // for GPS_Math_Known_Datum_To_WGS84_M, GPS_Math_Swiss_EN_To_WGS84, GPS_Math_UKOSMap_To_WGS84_H, GPS_Math_UTM_EN_To_Known_Datum
+#include "defs.h"                  // for case_ignore_strcmp, fatal, grid_type, KPH_TO_MPS, MPH_TO_MPS, warning, FEET_TO_METERS, KNOTS_TO_MPS, kDatumWGS84, FATHOMS_TO_METERS, MILES_TO_METERS, NMILES_TO_METERS, parse_coordinates, CSTR, parse_distance, parse_double, parse_integer, parse_speed
+#include "jeeps/gpsmath.h"         // for GPS_Math_Known_Datum_To_WGS84_M, GPS_Math_Swiss_EN_To_WGS84, GPS_Math_UKOSMap_To_WGS84_H, GPS_Math_UTM_EN_To_Known_Datum
+
+
+/*
+ * parse_integer
+ *
+ *  str:     input string
+ *  id:      identifier for error messages
+ *  ok:      conversion status.
+ *           if nullptr any conversion errors will fatal.
+ *  end:     unconverted trailing portion of string.
+ *           if nullptr a non-empty trailing portion will cause a conversion error.
+ *  base:    conversion base
+ */
+
+int parse_integer(const QString& str, const QString& id, bool* ok, QString* end, int base)
+{
+  auto ss = str.toStdString();
+  size_t pos = 0;
+  int result = 0;
+  try {
+    result = stoi(ss, &pos, base);
+  } catch (const std::invalid_argument&) {
+    if (ok == nullptr) {
+      fatal("%s: conversion to integer failed: invalid argument \"%s\".\n",
+            qPrintable(id), qPrintable(str));
+    } else {
+      *ok = false;
+      return 0;
+    }
+  } catch (const std::out_of_range&) {
+    if (ok == nullptr) {
+      fatal("%s: conversion to integer failed: out of range \"%s\".\n",
+            qPrintable(id), qPrintable(str));
+    } else {
+      *ok = false;
+      return 0;
+    }
+  } catch (...) {
+    if (ok == nullptr) {
+      fatal("%s: conversion to integer failed: unknown exception \"%s\".\n",
+            qPrintable(id), qPrintable(str));
+    } else {
+      *ok = false;
+      return 0;
+    }
+  }
+
+  QString remainder = QString::fromStdString(ss.erase(0, pos));
+  if ((end == nullptr) && !remainder.trimmed().isEmpty()) {
+    if (ok == nullptr) {
+      fatal("%s: conversion to integer failed: conversion of \"%s\" failed due to unexpected trailing data \"%s\".\n",
+            qPrintable(id), qPrintable(str), qPrintable(remainder));
+    } else {
+      *ok = false;
+      return 0;
+    }
+  }
+  if (end != nullptr) { // return possibly empty trailing portion of str
+    *end = remainder;
+  }
+
+  if (ok != nullptr) {
+    *ok = true;
+  }
+
+  return result;
+}
+
+/*
+ * parse_double
+ *
+ *  str:     input string
+ *  id:      identifier for error messages
+ *  ok:      conversion status.
+ *           if nullptr any conversion errors will fatal.
+ *  end:     unconverted trailing portion of string.
+ *           if nullptr a non-empty trailing portion will cause a conversion error.
+ *  base:    conversion base
+ *
+ */
+
+double parse_double(const QString& str, const QString& id, bool* ok, QString* end)
+{
+  auto ss = str.toStdString();
+  size_t pos = 0;
+  double result = 0.0;
+  try {
+    result = stod(ss, &pos);
+  } catch (const std::invalid_argument&) {
+    if (ok == nullptr) {
+      fatal("%s: conversion to double failed: invalid argument \"%s\".\n",
+            qPrintable(id), qPrintable(str));
+    } else {
+      *ok = false;
+      return 0.0;
+    }
+  } catch (const std::out_of_range&) {
+    if (ok == nullptr) {
+      fatal("%s: conversion to double failed: out of range \"%s\".\n",
+            qPrintable(id), qPrintable(str));
+    } else {
+      *ok = false;
+      return 0.0;
+    }
+  } catch (...) {
+    if (ok == nullptr) {
+      fatal("%s: conversion to double failed: unknown exception \"%s\".\n",
+            qPrintable(id), qPrintable(str));
+    } else {
+      *ok = false;
+      return 0.0;
+    }
+  }
+
+  QString remainder = QString::fromStdString(ss.erase(0, pos));
+  if ((end == nullptr) && !remainder.trimmed().isEmpty()) {
+    if (ok == nullptr) {
+      fatal("%s: conversion to double failed: conversion of \"%s\" failed due to unexpected trailing data \"%s\".\n",
+            qPrintable(id), qPrintable(str), qPrintable(remainder));
+    } else {
+      *ok = false;
+      return 0.0;
+    }
+  }
+  if (end != nullptr) { // return possibly empty trailing portion of str
+    *end = remainder;
+  }
+
+  if (ok != nullptr) {
+    *ok = true;
+  }
+
+  return result;
+}
 
 /*
  * parse_distance:
  */
 
 int
-parse_distance(const char* str, double* val, double scale, const char* module)
+parse_distance(const QString& str, double* val, double scale, const char* module)
 {
-  char* unit;
-
-  if ((str == nullptr) || (*str == '\0')) {
+  if (str.isEmpty()) {
     return 0;
   }
-  *val = strtod(str, &unit);
-  if (unit == nullptr) {
-    fatal("%s: Unconvertible numeric value (%s)!\n", module, str);
-  }
+
+  QString unit;
+  constexpr bool* dieonfailure = nullptr;
+  *val = parse_double(str, module, dieonfailure, &unit);
 
   if (fabs(*val) + 1 >= 1.0e25) {
     return 0; /* not only Garmin uses this as 'unknown value' */
   }
 
-  while (isspace(*unit)) {
-    unit++;
-  }
+  unit = unit.trimmed();
 
-  if (*unit == '\0') {
+  if (unit.isEmpty()) {
     *val *= scale;
     return 1;
   }
@@ -81,17 +212,11 @@ parse_distance(const char* str, double* val, double scale, const char* module)
   } else if (case_ignore_strcmp(unit, "fa") == 0) {
     *val = FATHOMS_TO_METERS(*val);
   } else {
-    fatal("%s: Unsupported distance unit in item '%s'!\n", module, str);
+    fatal("%s: Unsupported distance unit in item '%s'!\n", module, qPrintable(str));
   }
   return 2;
 }
 
-int
-parse_distance(const QString& str, double* val, double scale, const char* module)
-{
-  return parse_distance(CSTR(str), val, scale, module);
-}
-
 /*
  * parse_speed:
  *
@@ -101,24 +226,20 @@ parse_distance(const QString& str, double* val, double scale, const char* module
  *  module:  calling module, i.e. "garmin_txt"
  */
 int
-parse_speed(const char* str, double* val, const double scale, const char* module)
+parse_speed(const QString& str, double* val, const double scale, const char* module)
 {
-  char* unit;
 
-  if ((str == nullptr) || (*str == '\0')) {
+  if (str.isEmpty()) {
     return 0;
   }
 
-  *val = strtod(str, &unit);
-  if (unit == nullptr) {
-    fatal("%s: Unconvertible numeric value (%s)!\n", module, str);
-  }
+  QString unit;
+  constexpr bool* dieonfailure = nullptr;
+  *val = parse_double(str, module, dieonfailure, &unit);
 
-  while (isspace(*unit)) {
-    unit++;
-  }
+  unit = unit.trimmed();
 
-  if (*unit == '\0') {
+  if (unit.isEmpty()) {
     *val *= scale;
     return 1;
   }
@@ -142,18 +263,12 @@ parse_speed(const char* str, double* val, const double scale, const char* module
   } else if (case_ignore_strcmp(unit, "mih") == 0) {
     *val = MPH_TO_MPS(*val);
   } else {
-    warning("%s: Unsupported speed unit '%s' in item '%s'!\n", module, unit, str);
+    warning("%s: Unsupported speed unit '%s' in item '%s'!\n", module, qPrintable(unit), qPrintable(str));
   }
 
   return 2;
 }
 
-int
-parse_speed(const QString& str, double* val, const double scale, const char* module)
-{
-  return parse_speed(CSTR(str), val, scale, module);
-}
-
 /*
  * Convert string 'str' into geodetic latitude & longitude values. The format
  * will be interpreted depending on 'grid' parameter.
index 7ca2210649c22cc3e4ef9e53b00567d8e28120bc..713b15fb8af12f1b73070946b5d6edf032560da4 100644 (file)
@@ -229,7 +229,7 @@ void PolygonFilter::process()
   QString line;
 
   gpsbabel::TextStream stream;
-  stream.open(polyfileopt.get(), QIODevice::ReadOnly, MYNAME);
+  stream.open(polyfileopt, QIODevice::ReadOnly, MYNAME);
 
   double olat = BADVAL;
   double olon = BADVAL;
index 40915eb049e5b0a2208a8a5c26373e7888fb063f..2b84d8d5a0fa4c7b09d3e0a66854753863e560e8 100644 (file)
--- a/polygon.h
+++ b/polygon.h
@@ -28,7 +28,7 @@
 
 #include "defs.h"    // for ARG_NOMINMAX, arglist_t, ARGTYPE_BOOL, ARGTYPE_FILE
 #include "filter.h"  // for Filter
-#include "option.h"  // for OptionBool, OptionCString
+#include "option.h"  // for OptionBool, OptionString
 
 #if FILTERS_ENABLED
 
@@ -58,7 +58,7 @@ private:
 
   /* Data Members */
 
-  OptionCString polyfileopt;
+  OptionString polyfileopt;
   OptionBool exclopt;
 
   QVector<arglist_t> args = {
index c334315aae1ec42bea65de7b65a93ad9499c5606..45b032bc570ac6246cb370f2bac70eb8305c0d04 100644 (file)
@@ -22,7 +22,7 @@
 #include "position.h"
 
 #include <cmath>                // for abs
-#include <cstdlib>              // for strtod, abs
+#include <cstdlib>              // for abs
 
 #include <QList>                // for QList
 #include <QtGlobal>             // for qRound64, qint64
@@ -112,15 +112,15 @@ void PositionFilter::init()
   max_diff_time = 0;
   check_time = false;
 
-  if (distopt != nullptr) {
+  if (distopt) {
     if (parse_distance(distopt, &pos_dist, kMetersPerFoot, MYNAME) == 0) {
       fatal(MYNAME ": No distance specified with distance option.\n");
     }
   }
 
-  if (timeopt != nullptr) {
+  if (timeopt) {
     check_time = true;
-    max_diff_time = qRound64(strtod(timeopt, nullptr) * 1000.0);
+    max_diff_time = qRound64(timeopt.get_result() * 1000.0);
   }
 }
 
index 8ce40dec8bf6c1e87177962d4c0d392fef622445..98b8e384e5adf624cca6995c66be0b9ba03f76a9 100644 (file)
@@ -29,7 +29,7 @@
 
 #include "defs.h"     // for arglist_t, route_head (ptr only), ARG_NOMINMAX, ARGTYPE_FLOAT, ARGTYPE_REQUIRED, ARGTYPE_BOOL, Waypoint, WaypointList (ptr only)
 #include "filter.h"   // for Filter
-#include "option.h"  // for OptionCString, OptionBool
+#include "option.h"  // for OptionString, OptionBool
 
 
 #if FILTERS_ENABLED
@@ -64,15 +64,15 @@ private:
 
   double pos_dist{};
   qint64 max_diff_time{};
-  OptionCString distopt;
-  OptionCString timeopt;
+  OptionDouble distopt;
+  OptionDouble timeopt;
   OptionBool purge_duplicates;
   bool check_time{};
 
   QVector<arglist_t> args = {
     {
       "distance", &distopt, "Maximum positional distance",
-      nullptr, ARGTYPE_FLOAT | ARGTYPE_REQUIRED, ARG_NOMINMAX, nullptr
+      nullptr,  ARGTYPE_ALLOW_TRAILING_DATA | ARGTYPE_STRING | ARGTYPE_REQUIRED, ARG_NOMINMAX, nullptr
     },
     {
       "all", &purge_duplicates,
index 3e11c2fdeff536c26467ef82b810dd5a604cd172..64db76e513332b5e88cd54035366dae44c9392f4 100644 (file)
--- a/radius.cc
+++ b/radius.cc
 
 #include "radius.h"
 
-#include <cstdlib>          // for strtod
 #include <utility>          // for as_const
 
 #include <QString>          // for QString
 #include <QtGlobal>         // QAddConst<>::Type, foreach
 
-#include "defs.h"           // for Waypoint, del_marked_wpts, route_add_head, route_add_wpt, waypt_add, waypt_sort, waypt_swap, xstrtoi, route_head, WaypointList, kMilesPerKilometer
+#include "defs.h"           // for Waypoint, del_marked_wpts, route_add_head, route_add_wpt, waypt_add, waypt_sort, waypt_swap, route_head, WaypointList, kMilesPerKilometer
 #include "grtcirc.h"         // for gcdist, radtomiles
 
 
@@ -60,7 +59,7 @@ void RadiusFilter::process()
   }
 
   route_head* rte_head = nullptr;
-  if (routename != nullptr) {
+  if (routename) {
     rte_head = new route_head;
     rte_head->rte_name = routename;
     route_add_head(rte_head);
@@ -80,10 +79,10 @@ void RadiusFilter::process()
     delete static_cast<extra_data*>(wp->extra_data);
     wp->extra_data = nullptr;
 
-    if ((maxctarg != nullptr) && (i >= maxct)) {
+    if (maxctarg && (i >= maxct)) {
       delete wp;
     } else {
-      if (routename != nullptr) {
+      if (routename) {
         route_add_wpt(rte_head, wp);
       } else {
         waypt_add(wp);
@@ -97,25 +96,25 @@ void RadiusFilter::init()
 {
   pos_dist = 0;
 
-  if (distopt != nullptr) {
+  if (distopt) {
     if (parse_distance(distopt, &pos_dist, kMetersPerMile, MYNAME) == 0) {
       fatal(MYNAME ": No distance specified with distance option.\n");
     }
   }
 
-  if (maxctarg != nullptr) {
-    maxct = xstrtoi(maxctarg, nullptr, 10);
+  if (maxctarg) {
+    maxct = maxctarg.get_result();
   } else {
     maxct = 0;
   }
 
   home_pos = new Waypoint;
 
-  if (latopt != nullptr) {
-    home_pos->latitude = strtod(latopt, nullptr);
+  if (latopt) {
+    home_pos->latitude = latopt.get_result();
   }
-  if (lonopt != nullptr) {
-    home_pos->longitude = strtod(lonopt, nullptr);
+  if (lonopt) {
+    home_pos->longitude = lonopt.get_result();
   }
 }
 
index 5c99c995a4aa503737eb1230a91c2343d8454287..7f83d8929ce8aa9d7c559bfe5c65c0f89b3d1b35 100644 (file)
--- a/radius.h
+++ b/radius.h
@@ -28,7 +28,7 @@
 
 #include "defs.h"     // for arglist_t, ARG_NOMINMAX, ARGTYPE_FLOAT, ARGTYPE_REQUIRED, ARGTYPE_BOOL, ARGTYPE_INT, ARGTYPE_STRING, Waypoint
 #include "filter.h"   // for Filter
-#include "option.h"  // for OptionCString, OptionBool
+#include "option.h"  // for OptionString, OptionBool
 
 #if FILTERS_ENABLED
 
@@ -55,13 +55,13 @@ private:
   /* Data Members */
 
   double pos_dist{};
-  OptionCString distopt;
-  OptionCString latopt;
-  OptionCString lonopt;
+  OptionDouble distopt;
+  OptionDouble latopt;
+  OptionDouble lonopt;
   OptionBool exclopt;
   OptionBool nosort;
-  OptionCString maxctarg;
-  OptionCString routename;
+  OptionInt maxctarg;
+  OptionString routename;
   int maxct{};
 
   Waypoint* home_pos{};
@@ -77,7 +77,7 @@ private:
     },
     {
       "distance", &distopt, "Maximum distance from center",
-      nullptr, ARGTYPE_FLOAT | ARGTYPE_REQUIRED, ARG_NOMINMAX, nullptr
+      nullptr,  ARGTYPE_ALLOW_TRAILING_DATA | ARGTYPE_STRING | ARGTYPE_REQUIRED, ARG_NOMINMAX, nullptr
     },
     {
       "exclude", &exclopt,  "Exclude points close to center",
index b0e8033d0eff5cdc2f4f5bcfc14a57d50d80d6e5..8a684ed2d24f294c2e1c79694276dba2260e74bd 100644 (file)
--- a/random.cc
+++ b/random.cc
@@ -80,7 +80,7 @@ RandomFormat::random_set_generator()
 {
   generator = new std::mt19937;
   if (opt_seed) {
-    generator->seed(xstrtoi(opt_seed, nullptr, 10));
+    generator->seed(opt_seed.get_result());
   } else {
     generator->seed(gpsbabel_time);
   }
@@ -205,7 +205,7 @@ RandomFormat::read()
   Waypoint* prev = nullptr;
   QDateTime time = current_time().toUTC();
 
-  int points = (opt_points) ? xstrtoi(opt_points, nullptr, 10) : rand_int(128) + 1;
+  int points = opt_points? opt_points.get_result() : rand_int(128) + 1;
   if (doing_trks || doing_rtes) {
     head = new route_head;
     if (doing_trks) {
@@ -244,7 +244,7 @@ RandomFormat::rd_position_init(const QString& /*unused*/)
   random_set_generator();
   realtime = new realtime_data;
   if (opt_points) {
-    realtime->points = xstrtoi(opt_points, nullptr, 10);
+    realtime->points = opt_points.get_result();
   }
   realtime->time = current_time().toUTC();
 }
index 99b5ac60ab740c3ba13a5c3a723c6f1a93999a64..e41d212f9009f6aaeafb52214eedcd0827ad2173 100644 (file)
--- a/random.h
+++ b/random.h
@@ -29,7 +29,7 @@
 
 #include "defs.h"
 #include "format.h"   // for Format
-#include "option.h"   // for OptionCString, OptionBool
+#include "option.h"   // for OptionString, OptionBool
 
 
 class RandomFormat : public Format
@@ -84,8 +84,8 @@ private:
 
   /* Data Members */
 
-  OptionCString opt_points;
-  OptionCString opt_seed;
+  OptionInt opt_points;
+  OptionInt opt_seed;
   OptionBool opt_nodelay;
 
   QVector<arglist_t> random_args = {
index 6cb9768fd203c2c4719c938075d65ed4b1ed098e..fd76bfb05c27ff988a9e3e7042d36d2c1a8c2a22 100644 (file)
@@ -8,14 +8,14 @@ arc   Include Only Points Within Distance of Arc      https://www.gpsbabel.org/WEB_DOC_
 option arc     file    File containing vertices of arc file                            https://www.gpsbabel.org/WEB_DOC_DIR/filter_arc.html#fmt_arc_o_file
 option arc     rte     Route(s) are vertices of arc    boolean                         https://www.gpsbabel.org/WEB_DOC_DIR/filter_arc.html#fmt_arc_o_rte
 option arc     trk     Track(s) are vertices of arc    boolean                         https://www.gpsbabel.org/WEB_DOC_DIR/filter_arc.html#fmt_arc_o_trk
-option arc     distance        Maximum distance from arc       float                           https://www.gpsbabel.org/WEB_DOC_DIR/filter_arc.html#fmt_arc_o_distance
+option arc     distance        Maximum distance from arc       string                          https://www.gpsbabel.org/WEB_DOC_DIR/filter_arc.html#fmt_arc_o_distance
 option arc     exclude Exclude points close to the arc boolean                         https://www.gpsbabel.org/WEB_DOC_DIR/filter_arc.html#fmt_arc_o_exclude
 option arc     points  Use distance from vertices not lines    boolean                         https://www.gpsbabel.org/WEB_DOC_DIR/filter_arc.html#fmt_arc_o_points
 option arc     project Move waypoints to its projection on lines or vertices   boolean                         https://www.gpsbabel.org/WEB_DOC_DIR/filter_arc.html#fmt_arc_o_project
 radius Include Only Points Within Radius       https://www.gpsbabel.org/WEB_DOC_DIR/filter_radius.html
 option radius  lat     Latitude for center point (D.DDDDD)     float                           https://www.gpsbabel.org/WEB_DOC_DIR/filter_radius.html#fmt_radius_o_lat
 option radius  lon     Longitude for center point (D.DDDDD)    float                           https://www.gpsbabel.org/WEB_DOC_DIR/filter_radius.html#fmt_radius_o_lon
-option radius  distance        Maximum distance from center    float                           https://www.gpsbabel.org/WEB_DOC_DIR/filter_radius.html#fmt_radius_o_distance
+option radius  distance        Maximum distance from center    string                          https://www.gpsbabel.org/WEB_DOC_DIR/filter_radius.html#fmt_radius_o_distance
 option radius  exclude Exclude points close to center  boolean                         https://www.gpsbabel.org/WEB_DOC_DIR/filter_radius.html#fmt_radius_o_exclude
 option radius  nosort  Inhibit sort by distance to center      boolean                         https://www.gpsbabel.org/WEB_DOC_DIR/filter_radius.html#fmt_radius_o_nosort
 option radius  maxcount        Output no more than this number of points       integer         1               https://www.gpsbabel.org/WEB_DOC_DIR/filter_radius.html#fmt_radius_o_maxcount
@@ -25,7 +25,7 @@ option        interpolate     time    Time interval in seconds        float           0               https://www.gpsbabel.
 option interpolate     distance        Distance interval       string                          https://www.gpsbabel.org/WEB_DOC_DIR/filter_interpolate.html#fmt_interpolate_o_distance
 option interpolate     route   Interpolate routes instead      boolean                         https://www.gpsbabel.org/WEB_DOC_DIR/filter_interpolate.html#fmt_interpolate_o_route
 height Manipulate altitudes    https://www.gpsbabel.org/WEB_DOC_DIR/filter_height.html
-option height  add     Adds a constant value to every altitude float                           https://www.gpsbabel.org/WEB_DOC_DIR/filter_height.html#fmt_height_o_add
+option height  add     Adds a constant value to every altitude string                          https://www.gpsbabel.org/WEB_DOC_DIR/filter_height.html#fmt_height_o_add
 option height  wgs84tomsl      Converts WGS84 ellipsoidal height to orthometric height (MSL)   boolean                         https://www.gpsbabel.org/WEB_DOC_DIR/filter_height.html#fmt_height_o_wgs84tomsl
 track  Manipulate track lists  https://www.gpsbabel.org/WEB_DOC_DIR/filter_track.html
 option track   move    Correct trackpoint timestamps by a delta        string                          https://www.gpsbabel.org/WEB_DOC_DIR/filter_track.html#fmt_track_o_move
@@ -67,14 +67,14 @@ option      duplicate       location        Suppress duplicate waypoint based on coords     boolean
 option duplicate       all     Suppress all instances of duplicates    boolean                         https://www.gpsbabel.org/WEB_DOC_DIR/filter_duplicate.html#fmt_duplicate_o_all
 option duplicate       correct Use coords from duplicate points        boolean                         https://www.gpsbabel.org/WEB_DOC_DIR/filter_duplicate.html#fmt_duplicate_o_correct
 position       Remove Points Within Distance   https://www.gpsbabel.org/WEB_DOC_DIR/filter_position.html
-option position        distance        Maximum positional distance     float                           https://www.gpsbabel.org/WEB_DOC_DIR/filter_position.html#fmt_position_o_distance
+option position        distance        Maximum positional distance     string                          https://www.gpsbabel.org/WEB_DOC_DIR/filter_position.html#fmt_position_o_distance
 option position        all     Suppress all points close to other points       boolean                         https://www.gpsbabel.org/WEB_DOC_DIR/filter_position.html#fmt_position_o_all
 option position        time    Maximum time in seconds between two points      float                           https://www.gpsbabel.org/WEB_DOC_DIR/filter_position.html#fmt_position_o_time
 discard        Remove unreliable points with high hdop or vdop https://www.gpsbabel.org/WEB_DOC_DIR/filter_discard.html
 option discard hdop    Suppress points with higher hdop        float   -1.0                    https://www.gpsbabel.org/WEB_DOC_DIR/filter_discard.html#fmt_discard_o_hdop
 option discard vdop    Suppress points with higher vdop        float   -1.0                    https://www.gpsbabel.org/WEB_DOC_DIR/filter_discard.html#fmt_discard_o_vdop
 option discard hdopandvdop     Link hdop and vdop suppression with AND boolean                         https://www.gpsbabel.org/WEB_DOC_DIR/filter_discard.html#fmt_discard_o_hdopandvdop
-option discard sat     Minimum sats to keep points     integer -1.0                    https://www.gpsbabel.org/WEB_DOC_DIR/filter_discard.html#fmt_discard_o_sat
+option discard sat     Minimum sats to keep points     integer -1                      https://www.gpsbabel.org/WEB_DOC_DIR/filter_discard.html#fmt_discard_o_sat
 option discard fixnone Suppress points without fix     boolean                         https://www.gpsbabel.org/WEB_DOC_DIR/filter_discard.html#fmt_discard_o_fixnone
 option discard fixunknown      Suppress points with unknown fix        boolean                         https://www.gpsbabel.org/WEB_DOC_DIR/filter_discard.html#fmt_discard_o_fixunknown
 option discard elemin  Suppress points below given elevation in meters integer                         https://www.gpsbabel.org/WEB_DOC_DIR/filter_discard.html#fmt_discard_o_elemin
index 8904538b7a4e736551f0ad53926f0c287184e8f9..f1b93959a550a97ef9961733cba23a226237a23c 100644 (file)
@@ -210,7 +210,7 @@ option      gdb     roadbook        Include major turn points (with description) from calculated
 
 file   rwrwrw  garmin_txt      txt     Garmin MapSource - txt (tab delimited)  garmin_txt
        https://www.gpsbabel.org/WEB_DOC_DIR/fmt_garmin_txt.html
-option garmin_txt      date    Read/Write date format (i.e. yyyy/mm/dd)        string                          https://www.gpsbabel.org/WEB_DOC_DIR/fmt_garmin_txt.html#fmt_garmin_txt_o_date
+option garmin_txt      date    Read/Write date format (i.e. yyyy/mm/dd)        string  dd/mm/yyyy                      https://www.gpsbabel.org/WEB_DOC_DIR/fmt_garmin_txt.html#fmt_garmin_txt_o_date
 
 option garmin_txt      datum   GPS datum (def. WGS 84) string  WGS 84                  https://www.gpsbabel.org/WEB_DOC_DIR/fmt_garmin_txt.html#fmt_garmin_txt_o_datum
 
@@ -222,7 +222,7 @@ option      garmin_txt      prec    Precision of coordinates        integer 3                       https://www.gpsbabel
 
 option garmin_txt      temp    Temperature unit [c=Celsius, f=Fahrenheit]      string  c                       https://www.gpsbabel.org/WEB_DOC_DIR/fmt_garmin_txt.html#fmt_garmin_txt_o_temp
 
-option garmin_txt      time    Read/Write time format (i.e. HH:mm:ss xx)       string                          https://www.gpsbabel.org/WEB_DOC_DIR/fmt_garmin_txt.html#fmt_garmin_txt_o_time
+option garmin_txt      time    Read/Write time format (i.e. HH:mm:ss xx)       string  HH:mm:ss                        https://www.gpsbabel.org/WEB_DOC_DIR/fmt_garmin_txt.html#fmt_garmin_txt_o_time
 
 option garmin_txt      utc     Write timestamps with offset x to UTC time      integer         -23     +23     https://www.gpsbabel.org/WEB_DOC_DIR/fmt_garmin_txt.html#fmt_garmin_txt_o_utc
 
index 74a77d84a8898bd1acb11c5d80beca796529c13b..b736ed3737d7477be569467552bc55e937c0e3c2 100644 (file)
@@ -243,25 +243,22 @@ void ResampleFilter::init()
 {
 
   if (averageopt) {
-    bool ok;
-    average_count = QString(averageopt).toInt(&ok);
-    if (!ok || average_count < 2) {
+    average_count = averageopt.get_result();
+    if (average_count < 2) {
       fatal(FatalMsg() << MYNAME ": the average count must be greater than one.");
     }
   }
 
   if (decimateopt) {
-    bool ok;
-    decimate_count = QString(decimateopt).toInt(&ok);
-    if (!ok || decimate_count < 2) {
+    decimate_count = decimateopt.get_result();
+    if (decimate_count < 2) {
       fatal(FatalMsg() << MYNAME ": the decimate count must be greater than one.");
     }
   }
 
   if (interpolateopt) {
-    bool ok;
-    interpolate_count = QString(interpolateopt).toInt(&ok);
-    if (!ok || interpolate_count < 2) {
+    interpolate_count = interpolateopt.get_result();
+    if (interpolate_count < 2) {
       fatal(FatalMsg() << MYNAME ": the interpolate count must be greater than one.");
     }
     if (!averageopt || average_count < interpolate_count) {
index b7dae03465013259f01d2f9f4a0ad311ef7e90b7..b272526c745aedcde9d0d8e2680fa499468f972d 100644 (file)
@@ -30,7 +30,7 @@
 
 #include "defs.h"              // for arglist_t, ARGTYPE_INT, Waypoint, route_head
 #include "filter.h"            // for Filter
-#include "option.h"            // for OptionCString
+#include "option.h"            // for OptionString
 #include "src/core/nvector.h"  // for NVector
 
 
@@ -69,9 +69,9 @@ private:
   int decimate_count{0};
   int interpolate_count{0};
 
-  OptionCString decimateopt;
-  OptionCString interpolateopt;
-  OptionCString averageopt;
+  OptionInt decimateopt;
+  OptionInt interpolateopt;
+  OptionInt averageopt;
 
   QVector<arglist_t> args = {
     {
diff --git a/shape.h b/shape.h
index b65417f6ba0cc96ff97713b6d2ad92f7e17d4594..93f8ee691ee45b2b23bc572c583f5ddfc0cc6f2d 100644 (file)
--- a/shape.h
+++ b/shape.h
@@ -28,7 +28,7 @@
 
 #include "defs.h"               // for arglist_t, ARGTYPE_STRING, Waypoint, route_head, FF_CAP_RW_ALL, ff_cap, ff_type, ff_type_file
 #include "format.h"             // for Format
-#include "option.h"            // for OptionCString
+#include "option.h"            // for OptionString
 #if SHAPELIB_ENABLED
 #if HAVE_LIBSHAPE
 #  include <shapefil.h>
@@ -88,8 +88,8 @@ private:
   QString ofname;
   int nameFieldIdx{};  // the field index of the field with fieldName "name" in the output DBF.
 
-  OptionCString opt_name;
-  OptionCString opt_url;
+  OptionString opt_name;
+  OptionString opt_url;
 
   QVector<arglist_t> shp_args = {
     {
index 2e13c9f89ef7b8be82fdd4183081b3d0ae838d29..a4b013761f83b0bec6b64610bc66bef6c2872916 100644 (file)
@@ -23,6 +23,7 @@
     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 
+#include <algorithm>       // for max, min
 #include <cctype>          // for isprint
 #include <cmath>           // for cos, sin, atan2, pow, sqrt
 #include <cstdarg>         // for va_end, va_list, va_start
@@ -64,9 +65,6 @@
 #define res_PROTOCOL_ERR       -3
 #define res_NOTFOUND           -4
 
-#define MIN(X,Y) ((X) < (Y) ? (X) : (Y))
-#define MAX(X,Y) ((X) > (Y) ? (X) : (Y))
-
 
 void
 SkytraqBase::db(int l, const char* msg, ...)
@@ -436,19 +434,17 @@ SkytraqBase::skytraq_configure_logging() const
     0x00                       // reserved
   };
 
-  if (opt_configure_logging) {
-    if (*opt_configure_logging) {
-      unsigned int nn = sscanf(opt_configure_logging, "%u:%u:%u:%u", &tmin, &tmax, &dmin, &dmax);
-      if (nn>3) {
-        db(0, "Reconfiguring logging to: tmin=%u, tmax=%u, dmin=%u, dmax=%u\n", tmin, tmax, dmin, dmax);
-        be_write32(MSG_LOG_CONFIGURE_CONTROL+5, tmin);
-        be_write32(MSG_LOG_CONFIGURE_CONTROL+1, tmax);
-        be_write32(MSG_LOG_CONFIGURE_CONTROL+13, dmin);
-        be_write32(MSG_LOG_CONFIGURE_CONTROL+9, dmax);
-      } else {
-        db(1, MYNAME "Option usage: configlog=tmin:tmax:dmin:dmax");
-        return -1;
-      }
+  if (!opt_configure_logging.isEmpty()) {
+    unsigned int nn = sscanf(opt_configure_logging.get().toUtf8(), "%u:%u:%u:%u", &tmin, &tmax, &dmin, &dmax);
+    if (nn>3) {
+      db(0, "Reconfiguring logging to: tmin=%u, tmax=%u, dmin=%u, dmax=%u\n", tmin, tmax, dmin, dmax);
+      be_write32(MSG_LOG_CONFIGURE_CONTROL+5, tmin);
+      be_write32(MSG_LOG_CONFIGURE_CONTROL+1, tmax);
+      be_write32(MSG_LOG_CONFIGURE_CONTROL+13, dmin);
+      be_write32(MSG_LOG_CONFIGURE_CONTROL+9, dmax);
+    } else {
+      db(1, MYNAME "Option usage: configlog=tmin:tmax:dmin:dmax");
+      return -1;
     }
   }
 
@@ -523,7 +519,7 @@ SkytraqBase::gpstime_to_qdatetime(int week, int sec) const
    */
   qint64 gps_timet = 315964800;     /* Jan 06 1980 0:00 UTC */
 
-  int week_rollover = xstrtoi(opt_gps_week_rollover, nullptr, 10);
+  int week_rollover = opt_gps_week_rollover.get_result();
   if (week_rollover < 0) {
     int current_week = (QDateTime::currentSecsSinceEpoch() - gps_timet)/
                        (7*SECONDS_PER_DAY);
@@ -531,7 +527,7 @@ SkytraqBase::gpstime_to_qdatetime(int week, int sec) const
   }
   gps_timet += (week+week_rollover*1024)*7*SECONDS_PER_DAY + sec;
 
-  int override = xstrtoi(opt_gps_utc_offset, nullptr, 10);
+  int override = opt_gps_utc_offset.get_result();
   if (override) {
     gps_timet -= override;
     return QDateTime::fromSecsSinceEpoch(gps_timet, QtUTC);
@@ -866,7 +862,7 @@ SkytraqBase::skytraq_read_single_sector(unsigned int sector, uint8_t* buf) const
   rd_char(&errors);
   rd_char(&errors);
   rd_char(&errors);
-  skytraq_set_baud(xstrtoi(opt_dlbaud, nullptr, 10));
+  skytraq_set_baud(opt_dlbaud.get_result());
 #endif
 
   cs = skytraq_calc_checksum(buf, i);
@@ -945,9 +941,9 @@ SkytraqBase::skytraq_read_tracks() const
   int rc;
   int got_sectors;
   int total_sectors_read = 0;
-  int read_at_once = MAX(xstrtoi(opt_read_at_once, nullptr, 10), 1);
-  int opt_first_sector_val = xstrtoi(opt_first_sector, nullptr, 10);
-  int opt_last_sector_val = xstrtoi(opt_last_sector, nullptr, 10);
+  int read_at_once = std::max(opt_read_at_once.get_result(), 1);
+  int opt_first_sector_val = opt_first_sector.get_result();
+  int opt_last_sector_val = opt_last_sector.get_result();
   int multi_read_supported = 1;
   gbfile* dumpfile = nullptr;
 
@@ -971,9 +967,9 @@ SkytraqBase::skytraq_read_tracks() const
        sectors_used_b = (log_wr_ptr + SECTOR_SIZE - 1) / SECTOR_SIZE;
        if (sectors_used_a != sectors_used_b) {
                db(1, "Warning: device reported inconsistent number of used sectors (a=%i, b=%i), "\
-                  "using max=%i\n", sectors_used_a, sectors_used_b, MAX(sectors_used_a, sectors_used_b));
+                  "using max=%i\n", sectors_used_a, sectors_used_b, std::max(sectors_used_a, sectors_used_b));
        }
-       sectors_used = MAX(sectors_used_a, sectors_used_b);
+       sectors_used = std::max(sectors_used_a, sectors_used_b);
   */
   if (opt_last_sector_val < 0) {
     sectors_used = sectors_total - sectors_free + 1 /*+5*/;
@@ -992,7 +988,7 @@ SkytraqBase::skytraq_read_tracks() const
   // m.ad/090930: removed code that tried reducing read_at_once if necessary since doesn't work with xmalloc
 
   if (opt_dump_file) {
-    dumpfile = gbfopen(opt_dump_file.get(), "w", MYNAME);
+    dumpfile = gbfopen(opt_dump_file, "w", MYNAME);
   }
 
   db(1, MYNAME ": Reading log data from device...\n");
@@ -1000,23 +996,23 @@ SkytraqBase::skytraq_read_tracks() const
   db(1, MYNAME ": opt_last_sector_val=%d\n", opt_last_sector_val);
   for (int i = opt_first_sector_val; i < sectors_used; i += got_sectors) {
     for (t = 0, got_sectors = 0; (t < SECTOR_RETRIES) && (got_sectors <= 0); t++) {
-      if (xstrtoi(opt_read_at_once, nullptr, 10) == 0  ||  multi_read_supported == 0) {
+      if (opt_read_at_once.get_result() == 0  ||  multi_read_supported == 0) {
         rc = skytraq_read_single_sector(i, buffer);
         if (rc == res_OK) {
           got_sectors = 1;
         }
       } else {
         /* Try to read read_at_once sectors at once.
-         * If tere aren't any so many interesting ones, read the remainder (sectors_used-i).
+         * If there aren't so many interesting ones, read the remainder (sectors_used-i).
          * And read at least 1 sector.
          */
-        read_at_once = MAX(MIN(read_at_once, sectors_used-i), 1);
+        read_at_once = std::max(std::min(read_at_once, sectors_used-i), 1);
 
         rc = skytraq_read_multiple_sectors(i, read_at_once, buffer);
         switch (rc) {
         case res_OK:
           got_sectors = read_at_once;
-          read_at_once = MIN(read_at_once*2, xstrtoi(opt_read_at_once, nullptr, 10));
+          read_at_once = std::min(read_at_once*2, opt_read_at_once.get_result());
           break;
 
         case res_NACK:
@@ -1027,7 +1023,7 @@ SkytraqBase::skytraq_read_tracks() const
 
         default:
           /* On failure, try with less sectors */
-          read_at_once = MAX(read_at_once/2, 1);
+          read_at_once = std::max(read_at_once/2, 1);
         }
       }
     }
@@ -1073,7 +1069,7 @@ SkytraqBase::skytraq_probe() const
 {
   int baud_rates[] = { 9600, 230400, 115200, 57600, 4800, 19200, 38400 };
   int baud_rates_count = sizeof(baud_rates)/sizeof(baud_rates[0]);
-  int initbaud = xstrtoi(opt_initbaud, nullptr, 10);
+  int initbaud = opt_initbaud.get_result();
   uint8_t MSG_QUERY_SOFTWARE_VERSION[2] = { 0x02, 0x01 };
   struct {
     uint8_t id;
@@ -1164,9 +1160,9 @@ SkytraqBase::skytraq_set_location() const
   uint8_t MSG_SET_LOCATION[17] = { 0x36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
   uint8_t MSG_GET_LOCATION = 0x35;
 
-  db(3, MYNAME ": set_location='%s'\n", qPrintable(opt_set_location.get()));
+  db(3, MYNAME ": set_location='%s'\n", qPrintable(opt_set_location));
 
-  sscanf(opt_set_location, "%lf:%lf", &lat, &lng);
+  sscanf(opt_set_location.get().toUtf8(), "%lf:%lf", &lat, &lng);
   le_write_double(&MSG_SET_LOCATION[1], lat);
   le_write_double(&MSG_SET_LOCATION[9], lng);
   for (unsigned char i : MSG_SET_LOCATION) {
@@ -1218,13 +1214,13 @@ SkytraqBase::skytraq_read() const
     return;
   }
 
-  int dlbaud = xstrtoi(opt_dlbaud, nullptr, 10);
+  int dlbaud = opt_dlbaud.get_result();
   if (dlbaud != 0  &&  dlbaud != skytraq_baud) {
     skytraq_set_baud(dlbaud);
   }
 
   // read device unless no-output=true and dump-file=0 (i.e. no data needed at all)
-  if (!opt_no_output ||  opt_dump_file != nullptr) {
+  if (!opt_no_output ||  opt_dump_file) {
     skytraq_read_tracks();
   }
 
@@ -1260,8 +1256,8 @@ SkytraqfileFormat::read()
 {
   read_state st;
   int got_bytes;
-  int opt_first_sector_val = xstrtoi(opt_first_sector, nullptr, 10);
-  int opt_last_sector_val = xstrtoi(opt_last_sector, nullptr, 10);
+  int opt_first_sector_val = opt_first_sector.get_result();
+  int opt_last_sector_val = opt_last_sector.get_result();
 
   state_init(&st);
   auto* buffer = (uint8_t*) xmalloc(SECTOR_SIZE);
@@ -1378,7 +1374,7 @@ void MinihomerFormat::miniHomer_get_poi() const
  * -1 in case of errors
  *  the number of the POI will not be checked - if it is not correct, miniHome will send NACK
  */
-int MinihomerFormat::miniHomer_set_poi(uint16_t poinum, const char* opt_poi) const
+int MinihomerFormat::miniHomer_set_poi(uint16_t poinum, const QString& opt_poi) const
 {
 #define MSG_SET_POI_SIZE (sizeof(uint8_t)+sizeof(uint16_t)+3*sizeof(double)+sizeof(uint8_t))
   uint8_t MSG_SET_POI[MSG_SET_POI_SIZE] = {
@@ -1397,33 +1393,31 @@ int MinihomerFormat::miniHomer_set_poi(uint16_t poinum, const char* opt_poi) con
 
 
   int result = 0;              // result will be 0 if opt_poi isn't set
-  if (opt_poi) {       // first check opt_poi
-    if (*opt_poi) {
-      lat=lng=alt=0.0;
-      /*
-       * parse format of <lat>:<lng>[:alt]
-       * we assume at least two elements in the value string
-       */
-      int n = sscanf(opt_poi, "%lf:%lf:%lf", &lat, &lng, &alt);
-      if (n >= 2) {
-        db(3, "found %d elems '%s':poi=%s@%d, lat=%f, lng=%f, alt=%f\n", n, opt_poi, poinames[poinum], poinum, lat, lng, alt);
-        lla2ecef(lat, lng, alt, &ecef_x, &ecef_y, &ecef_z);
-        db(1, MYNAME ": set POI[%s]='%f %f %f/%f %f %f'\n", poinames[poinum], lat, lng, alt, ecef_x, ecef_y, ecef_z);
-        be_write16(MSG_SET_POI+1, poinum);
-        be_write_double(MSG_SET_POI+3, ecef_x);
-        be_write_double(MSG_SET_POI+11, ecef_y);
-        be_write_double(MSG_SET_POI+19, ecef_z);
-        MSG_SET_POI[27]=0;
-        if (skytraq_wr_msg_verify((uint8_t*)&MSG_SET_POI, sizeof(MSG_SET_POI)) == res_OK) {
-          result=1;
-        } else {
-          warning(MYNAME ": cannot set poi %d '%s'\n", poinum, poinames[poinum]);
-          result=-1;
-        }
+  if (!opt_poi.isEmpty()) {    // first check opt_poi
+    lat=lng=alt=0.0;
+    /*
+     * parse format of <lat>:<lng>[:alt]
+     * we assume at least two elements in the value string
+     */
+    int n = sscanf(opt_poi.toUtf8(), "%lf:%lf:%lf", &lat, &lng, &alt);
+    if (n >= 2) {
+      db(3, "found %d elems '%s':poi=%s@%d, lat=%f, lng=%f, alt=%f\n", n, qPrintable(opt_poi), poinames[poinum], poinum, lat, lng, alt);
+      lla2ecef(lat, lng, alt, &ecef_x, &ecef_y, &ecef_z);
+      db(1, MYNAME ": set POI[%s]='%f %f %f/%f %f %f'\n", poinames[poinum], lat, lng, alt, ecef_x, ecef_y, ecef_z);
+      be_write16(MSG_SET_POI+1, poinum);
+      be_write_double(MSG_SET_POI+3, ecef_x);
+      be_write_double(MSG_SET_POI+11, ecef_y);
+      be_write_double(MSG_SET_POI+19, ecef_z);
+      MSG_SET_POI[27]=0;
+      if (skytraq_wr_msg_verify((uint8_t*)&MSG_SET_POI, sizeof(MSG_SET_POI)) == res_OK) {
+        result=1;
       } else {
-        warning(MYNAME ": argument to %s needs to be like <lat>:<lng>[:<alt>]\n", poinames[poinum]);
+        warning(MYNAME ": cannot set poi %d '%s'\n", poinum, poinames[poinum]);
         result=-1;
       }
+    } else {
+      warning(MYNAME ": argument to %s needs to be like <lat>:<lng>[:<alt>]\n", poinames[poinum]);
+      result=-1;
     }
   }
   return result;
@@ -1441,7 +1435,6 @@ MinihomerFormat::rd_deinit()
   skytraq_rd_deinit();
   mhport.clear();
 }
-#define SETPOI(poinum, poiname) if (opt_set_poi_##poiname )  {miniHomer_set_poi(poinum, opt_set_poi_##poiname);}
 void
 MinihomerFormat::read()
 {
index 291cb25507dec63baa4ff2f382ebdef0e9a22228..4ded87e57952bd5035cb1e110ed432db1a1e13fe 100644 (file)
--- a/skytraq.h
+++ b/skytraq.h
@@ -35,7 +35,7 @@
 #include "defs.h"
 #include "format.h"   // for Format
 #include "gbfile.h"   // for gbfile
-#include "option.h"   // for OptionCString, OptionBool
+#include "option.h"   // for OptionString, OptionBool
 
 
 class SkytraqBase
@@ -152,17 +152,17 @@ protected:
   int skytraq_baud = 0;                /* detected baud rate */
 
   OptionBool    opt_erase;             /* erase after read? (0/1) */
-  OptionCString opt_initbaud;          /* baud rate used to init device */
-  OptionCString opt_dlbaud;            /* baud rate used for downloading tracks */
-  OptionCString opt_read_at_once;      /* number of sectors to read at once (Venus6 only) */
-  OptionCString opt_first_sector;      /* first sector to be read from the device (default: 0) */
-  OptionCString opt_last_sector;       /* last sector to be read from the device (default: smart read everything) */
-  OptionCString opt_dump_file;         /* dump raw data to this file (optional) */
+  OptionInt opt_initbaud;              /* baud rate used to init device */
+  OptionInt opt_dlbaud;                /* baud rate used for downloading tracks */
+  OptionInt opt_read_at_once;  /* number of sectors to read at once (Venus6 only) */
+  OptionInt opt_first_sector;  /* first sector to be read from the device (default: 0) */
+  OptionInt opt_last_sector;   /* last sector to be read from the device (default: smart read everything) */
+  OptionString opt_dump_file;          /* dump raw data to this file (optional) */
   OptionBool    opt_no_output;         /* disable output? (0/1) */
-  OptionCString opt_set_location;      /* set if the "targetlocation" options was used */
-  OptionCString opt_configure_logging;
-  OptionCString opt_gps_utc_offset;
-  OptionCString opt_gps_week_rollover;
+  OptionString opt_set_location;       /* set if the "targetlocation" options was used */
+  OptionString opt_configure_logging;
+  OptionInt opt_gps_utc_offset;
+  OptionInt opt_gps_week_rollover;
 };
 
 class SkytraqFormat : public Format, private SkytraqBase
@@ -337,15 +337,15 @@ private:
 
   static void lla2ecef(double lat, double lng, double alt, double* ecef_x, double* ecef_y, double* ecef_z);
   void miniHomer_get_poi() const;
-  int miniHomer_set_poi(uint16_t poinum, const char* opt_poi) const;
+  int miniHomer_set_poi(uint16_t poinum, const QString& opt_poi) const;
 
   /* Data Members */
 
-  OptionCString opt_set_poi_home;      /* set if a "poi" option was used */
-  OptionCString opt_set_poi_car;       /* set if a "poi" option was used */
-  OptionCString opt_set_poi_boat;      /* set if a "poi" option was used */
-  OptionCString opt_set_poi_heart;     /* set if a "poi" option was used */
-  OptionCString opt_set_poi_bar;       /* set if a "poi" option was used */
+  OptionString opt_set_poi_home;       /* set if a "poi" option was used */
+  OptionString opt_set_poi_car;        /* set if a "poi" option was used */
+  OptionString opt_set_poi_boat;       /* set if a "poi" option was used */
+  OptionString opt_set_poi_heart;      /* set if a "poi" option was used */
+  OptionString opt_set_poi_bar;        /* set if a "poi" option was used */
 
   QVector<arglist_t> miniHomer_args = {
     { "baud",         &opt_dlbaud,        "Baud rate used for download", "115200", ARGTYPE_INT, "0", "115200", nullptr },
index 18522b90c4fe6ebafdb6f739cc1878ae9153ad4b..951696607b28c80da8d13f372d1fc085665d55d1 100644 (file)
@@ -57,7 +57,6 @@
 */
 
 #include <cassert>
-#include <cstdlib>              // for strtod, strtol
 #include <iterator>             // for prev
 
 #include <QDateTime>            // for QDateTime
@@ -287,11 +286,11 @@ void SimplifyRouteFilter::init()
 
   switch (limit_basis) {
   case limit_basis_t::count:
-    count = strtol(countopt, nullptr, 10);
+    count = countopt.get_result();
     break;
   case limit_basis_t::error: {
     if (metric == metric_t::relative) {
-      error = strtod(erroropt, nullptr);
+      error = erroropt.get_result();
     } else {
       if (parse_distance(erroropt, &error, kMetersPerMile, MYNAME) == 0) {
         fatal(MYNAME ": No value specified with error option.\n");
index 4954e8536d5d3d27c7cee5fcd8a62fe59aab7064..5852b03abbb686061f3bbadcfb342c1935d03589 100644 (file)
@@ -65,7 +65,7 @@
 
 #include "defs.h"
 #include "filter.h"  // for Filter
-#include "option.h"  // for OptionBool, OptionCString
+#include "option.h"  // for OptionBool, OptionString
 
 
 #if FILTERS_ENABLED
@@ -119,8 +119,8 @@ private:
   limit_basis_t limit_basis{limit_basis_t::error};
   metric_t metric{metric_t::crosstrack};
 
-  OptionCString countopt;
-  OptionCString erroropt;
+  OptionInt countopt;
+  OptionDouble erroropt;
   OptionBool xteopt;
   OptionBool lenopt;
   OptionBool relopt;
@@ -132,7 +132,7 @@ private:
     },
     {
       "error", &erroropt, "Maximum error", nullptr,
-      ARGTYPE_STRING | ARGTYPE_END_REQ | ARGTYPE_END_EXCL, "0", nullptr, nullptr
+       ARGTYPE_ALLOW_TRAILING_DATA | ARGTYPE_STRING | ARGTYPE_END_REQ | ARGTYPE_END_EXCL, "0", nullptr, nullptr
     },
     {
       "crosstrack", &xteopt, "Use cross-track error (default)", nullptr,
index 13b4f8f4138f30f13547ac3c907b73cc4607a06e..4a9828a9079f5c3288c84a59939cd622410f7b17 100644 (file)
@@ -107,7 +107,7 @@ void StackFilter::init()
   }
 
   if (opt_depth) {
-    swapdepth = xstrtoi(opt_depth, nullptr, 10);
+    swapdepth = opt_depth.get_result();
   }
   if (opt_push) {
     if (opt_pop || opt_append || opt_discard || opt_replace ||
index 7418275e5caf8d1dddc6bfcdfbf0d9cbf65f25f5..8fa3fc11c68ed03981184d38444680da928a7660 100644 (file)
@@ -28,7 +28,7 @@
 
 #include "defs.h"    // for ARGTYPE_BOOL, ARG_NOMINMAX, ARGTYPE_BEGIN_EXCL
 #include "filter.h"  // for Filter
-#include "option.h"  // for OptionBool, OptionCString
+#include "option.h"  // for OptionBool, OptionString
 
 #if FILTERS_ENABLED
 
@@ -52,7 +52,7 @@ private:
   OptionBool opt_discard;
   OptionBool opt_replace;
   OptionBool opt_swap;
-  OptionCString opt_depth;
+  OptionInt opt_depth;
   OptionBool nowarn;
   int  warnings_enabled = 1;
   int  swapdepth = 0;
index 5b6ef91d5dc50f205cd9892f620007c81ac40ded..d9a7f255ac21da8ed20f18e269b5d51d92698154 100644 (file)
--- a/subrip.cc
+++ b/subrip.cc
@@ -78,17 +78,16 @@ SubripFormat::subrip_prevwp_pr(const Waypoint* waypointp)
             starttime.hour(), starttime.minute(), starttime.second(), starttime.msec(),
             endtime.hour(), endtime.minute(), endtime.second(), endtime.msec());
 
-  for (const char* c = static_cast<const char*>(opt_format); *c != '\0' ; c++) {
-    char fmt;
+  const QByteArray format = opt_format.get().toUtf8();
+  for (auto it = format.cbegin(), end = format.cend(); it != end; ++it) {
 
-    switch (*c) {
+    switch (*it) {
     case '%':
-      fmt = *++c;
-      if (fmt == '\0') {
-        fatal("No character after %% in subrip format");
+      if (++it == end) {
+        fatal("No character after %% in subrip format.\n");
       }
 
-      switch (fmt) {
+      switch (*it) {
       case 's':
         if (prevwpp->speed_has_value()) {
           gbfprintf(fout, "%2.1f", MPS_TO_KPH(prevwpp->speed_value()));
@@ -137,18 +136,19 @@ SubripFormat::subrip_prevwp_pr(const Waypoint* waypointp)
       break;
 
     case '\\':
-      fmt = *++c;
-      if (fmt == '\0') {
-        fatal("No character after \\ in subrip format");
+      if (++it == end) {
+        fatal("No character after \\ in subrip format.\n");
       }
-      switch (fmt) {
+
+      switch (*it) {
       case 'n':
         gbfprintf(fout, "\n");
         break;
       }
       break;
+
     default:
-      gbfwrite(c, 1, 1, fout);
+      gbfputc(*it, fout);
     }
   }
   gbfprintf(fout, "\n\n");
@@ -205,32 +205,32 @@ SubripFormat::wr_init(const QString& fname)
   vspeed = 0;
   gradient = 0;
 
-  if ((opt_gpstime == nullptr) != (opt_gpsdate == nullptr)) {
+  if (opt_gpstime != opt_gpsdate) {
     fatal(FatalMsg() << MYNAME ": Either both or neither of the gps_date and gps_time options must be supplied!");
   }
   gps_datetime = QDateTime();
-  if ((opt_gpstime != nullptr) && (opt_gpsdate != nullptr)) {
-    QDate gps_date = QDate::fromString(opt_gpsdate.get(), u"yyyyMMdd");
+  if (opt_gpstime && opt_gpsdate) {
+    QDate gps_date = QDate::fromString(opt_gpsdate, u"yyyyMMdd");
     if (!gps_date.isValid()) {
-      fatal(FatalMsg().nospace() << MYNAME ": option gps_date value (" << opt_gpsdate << ") is invalid.  Expected yyyymmdd.");
+      fatal(FatalMsg().nospace() << MYNAME ": option gps_date value (" << opt_gpsdate.get() << ") is invalid.  Expected yyyymmdd.");
     }
-    QTime gps_time = QTime::fromString(opt_gpstime.get(), u"HHmmss");
+    QTime gps_time = QTime::fromString(opt_gpstime, u"HHmmss");
     if (!gps_time.isValid()) {
-      gps_time = QTime::fromString(opt_gpstime.get(), u"HHmmss.z");
+      gps_time = QTime::fromString(opt_gpstime, u"HHmmss.z");
       if (!gps_time.isValid()) {
-        fatal(FatalMsg().nospace() << MYNAME ": option gps_time value (" << opt_gpstime << ") is invalid.  Expected hhmmss[.sss]");
+        fatal(FatalMsg().nospace() << MYNAME ": option gps_time value (" << opt_gpstime.get() << ") is invalid.  Expected hhmmss[.sss]");
       }
     }
     gps_datetime = QDateTime(gps_date, gps_time, QtUTC);
   }
 
   video_offset_ms = 0;
-  if (opt_videotime != nullptr) {
-    QTime video_time = QTime::fromString(opt_videotime.get(), u"HHmmss");
+  if (opt_videotime) {
+    QTime video_time = QTime::fromString(opt_videotime, u"HHmmss");
     if (!video_time.isValid()) {
-      video_time = QTime::fromString(opt_videotime.get(), u"HHmmss.z");
+      video_time = QTime::fromString(opt_videotime, u"HHmmss.z");
       if (!video_time.isValid()) {
-        fatal(FatalMsg().nospace() << MYNAME ": option video_time value (" << opt_videotime << ") is invalid.  Expected hhmmss[.sss].");
+        fatal(FatalMsg().nospace() << MYNAME ": option video_time value (" << opt_videotime.get() << ") is invalid.  Expected hhmmss[.sss].");
       }
     }
     video_offset_ms = video_time.msecsSinceStartOfDay();
index 018a88ca206924be8ce49d80d41692d96cb82c34..81d59008db4f55cdacededda16c697a30975149a 100644 (file)
--- a/subrip.h
+++ b/subrip.h
@@ -31,7 +31,7 @@
 #include "defs.h"
 #include "format.h"   // for Format
 #include "gbfile.h"   // for gbfprintf, gbfclose, gbfopen, gbfwrite, gbfile
-#include "option.h"   // for OptionCString
+#include "option.h"   // for OptionString
 
 
 class SubripFormat : public Format
@@ -65,10 +65,10 @@ private:
 
   /* Data Members */
 
-  OptionCString opt_videotime;
-  OptionCString opt_gpstime;
-  OptionCString opt_gpsdate;
-  OptionCString opt_format;
+  OptionString opt_videotime;
+  OptionString opt_gpstime;
+  OptionString opt_gpsdate;
+  OptionString opt_format;
   QDateTime gps_datetime;    // Date time corresponding to video video_offset_ms
   QDateTime video_datetime;  // Date time corresponding to video time 00:00:00,000.
   int video_offset_ms{0};
index 68a1f01ee46cdd5fb375ffd1b1929fd1a2d64d8f..0fbef8653940b644b3ed68d8cffc38e033581427 100644 (file)
@@ -17,6 +17,10 @@ gpsbabel -i gpx -f ${REFERENCE}/igc1_baro.gpx -i igc -f ${REFERENCE}/igc1_igc.ou
 sed '/^LXXXGenerated by GPSBabel Version/d' ${TMPDIR}/igc.out > ${TMPDIR}/igc_sed3.out
 compare ${REFERENCE}/igc1_3d.out ${TMPDIR}/igc_sed3.out
 
+gpsbabel -i gpx -f ${REFERENCE}/igc1_baro.gpx -i igc -f ${REFERENCE}/igc1_igc.out -o igc,timeadj=-129 -F ${TMPDIR}/igc.out
+sed '/^LXXXGenerated by GPSBabel Version/d' ${TMPDIR}/igc.out > ${TMPDIR}/igc_sed4.out
+compare ${REFERENCE}/igc1_3d.out ${TMPDIR}/igc_sed4.out
+
 
 gpsbabel -i igc -f ${REFERENCE}/igc2.igc -o gpx -F ${TMPDIR}/igc2~igc.gpx
 compare ${REFERENCE}/igc2_gpx.out ${TMPDIR}/igc2~igc.gpx
diff --git a/text.cc b/text.cc
index e288303d45afe8143dd54f738b092199ed7030e8..67b38029585843454ef6a6654ea7bb85aa42bb98 100644 (file)
--- a/text.cc
+++ b/text.cc
@@ -22,6 +22,8 @@
 #include "text.h"
 
 #include <QIODevice>               // for QIODevice, QIODevice::WriteOnly
+#include <QRegularExpression>      // for QRegularExpression
+#include <QRegularExpressionMatch> // for QRegularExpressionMatch
 #include <QString>                 // for QString, operator!=
 #include <QTextStream>             // for QTextStream
 #include <Qt>                      // for CaseInsensitive
@@ -49,6 +51,23 @@ TextFormat::wr_init(const QString& fname)
     file_out->open(fname, QIODevice::WriteOnly, MYNAME);
   }
   mkshort_handle = new MakeShort;
+
+  static const QRegularExpression re("^(?:ddd|dmm|dms)$");
+  assert(re.isValid());
+  if (re.match(opt_degformat).hasMatch()) {
+    degformat = opt_degformat.get().at(2).toLatin1();
+  } else {
+    fatal(MYNAME ": Unrecognized degformat %s, expected 'ddd', 'dmm' or 'dms'.\n", qPrintable(opt_degformat));
+  }
+
+  if (opt_altunits.get().startsWith('f')) {
+    altunits = 'f';
+  } else if (opt_altunits.get().startsWith('m')) {
+    altunits = 'm';
+  } else {
+    fatal(MYNAME ": Unrecognized altunits %s, expected 'f' for feet or 'm' for meters.\n", qPrintable(opt_altunits));
+  }
+
 }
 
 void
@@ -84,13 +103,13 @@ TextFormat::text_disp(const Waypoint* wpt)
   GPS_Math_WGS84_To_UTM_EN(wpt->latitude, wpt->longitude,
                            &utme, &utmn, &utmz, &utmzc);
   QString position = QStringLiteral("%1 (%2%3 %4 %5)")
-                     .arg(pretty_deg_format(wpt->latitude, wpt->longitude, degformat[2], " ", false))
+                     .arg(pretty_deg_format(wpt->latitude, wpt->longitude, degformat, " ", false))
                      .arg(utmz)
                      .arg(utmzc)
                      .arg(utme, 6, 'f', 0)
                      .arg(utmn, 7, 'f', 0);
   if (wpt->altitude != unknown_alt) {
-    position += QStringLiteral(" alt:%1").arg((int)((altunits[0]=='f') ? METERS_TO_FEET(wpt->altitude) : wpt->altitude));
+    position += QStringLiteral(" alt:%1").arg((int)((altunits == 'f') ? METERS_TO_FEET(wpt->altitude) : wpt->altitude));
   }
   QString sn = global_opts.synthesize_shortnames ? mkshort_handle->mkshort_from_wpt(wpt) : wpt->shortname;
   *file_out << sn.leftJustified(16) << "  " <<  position.rightJustified(59) << "\n";
@@ -156,7 +175,7 @@ TextFormat::text_disp(const Waypoint* wpt)
         if (logpart) {
           double lat = logpart->xml_attribute("lat").toDouble();
           double lon = logpart->xml_attribute("lon").toDouble();
-          *file_out << pretty_deg_format(lat, lon, degformat[2], " ", false) << "\n";
+          *file_out << pretty_deg_format(lat, lon, degformat, " ", false) << "\n";
         }
 
         logpart = curlog->xml_findfirst(u"groundspeak:text");
diff --git a/text.h b/text.h
index 69487256f3abf331764bb22a5dab9bf9c605a296..919d834ddd9b4534713aba374ea54d3599b66c3b 100644 (file)
--- a/text.h
+++ b/text.h
@@ -28,7 +28,7 @@
 #include "defs.h"
 #include "format.h"               // for Format
 #include "mkshort.h"              // for MakeShort
-#include "option.h"               // for OptionBool, OptionCString
+#include "option.h"               // for OptionBool, OptionString
 #include "src/core/textstream.h"  // for TextStream
 
 
@@ -68,9 +68,11 @@ private:
   OptionBool suppresssep;
   OptionBool txt_encrypt;
   OptionBool includelogs;
-  OptionCString degformat;
-  OptionCString altunits;
+  OptionString opt_degformat;
+  OptionString opt_altunits;
   OptionBool split_output;
+  char degformat{};
+  char altunits{};
   int waypoint_count{};
   QString output_name;
 
@@ -89,11 +91,11 @@ private:
       "Include groundspeak logs if present", nullptr, ARGTYPE_BOOL, ARG_NOMINMAX, nullptr
     },
     {
-      "degformat", &degformat,
+      "degformat", &opt_degformat,
       "Degrees output as 'ddd', 'dmm'(default) or 'dms'", "dmm", ARGTYPE_STRING, ARG_NOMINMAX, nullptr
     },
     {
-      "altunits", &altunits,
+      "altunits", &opt_altunits,
       "Units for altitude (f)eet or (m)etres", "m", ARGTYPE_STRING, ARG_NOMINMAX, nullptr
     },
     {
diff --git a/tpg.cc b/tpg.cc
index b1c25b63548b6a3fbcf0aba0397aa2d49e6cfa28..3e293c8516f17030760e7b3407e40a23e776e49a 100644 (file)
--- a/tpg.cc
+++ b/tpg.cc
@@ -61,7 +61,7 @@ TpgFormat::tpg_common_init()
 {
   tpg_datum_idx = GPS_Lookup_Datum_Index(tpg_datum_opt);
   if (tpg_datum_idx < 0) {
-    fatal(MYNAME ": Datum '%s' is not recognized.\n", qPrintable(tpg_datum_opt.get()));
+    fatal(MYNAME ": Datum '%s' is not recognized.\n", qPrintable(tpg_datum_opt));
   }
 }
 
diff --git a/tpg.h b/tpg.h
index 088fbab105eef9bb3cb1ab0557305976b1539f28..d84a478802ca6356e2c921ef428ae3214422195c 100644 (file)
--- a/tpg.h
+++ b/tpg.h
@@ -32,7 +32,7 @@
 #include "format.h"   // for Format
 #include "gbfile.h"   // for gbfile
 #include "mkshort.h"  // for MakeShort
-#include "option.h"   // for OptionCString
+#include "option.h"   // for OptionString
 
 
 class TpgFormat : public Format
@@ -74,7 +74,7 @@ private:
   gbfile* tpg_file_in{};
   gbfile* tpg_file_out{};
   MakeShort* mkshort_handle{};
-  OptionCString tpg_datum_opt;
+  OptionString tpg_datum_opt;
   int tpg_datum_idx{};
 
   int waypt_out_count{};
index d36d3cfe1d880db4392d7a2bc248ea357e7ed9ff..da88dba44eb23e6eb68fbd33c7be277f7c43e276 100644 (file)
@@ -27,7 +27,6 @@ static constexpr bool TRACKF_DBG = false;
 #include <cmath>                           // for nan
 #include <cstdio>                          // for printf
 #include <cstdlib>                         // for abs
-#include <cstring>                         // for strlen, strchr, strcmp
 #include <ctime>                           // for gmtime, strftime, time_t, tm
 #include <iterator>                        // for next
 #include <utility>                         // for as_const
@@ -74,7 +73,7 @@ int TrackFilter::trackfilter_opt_count()
   return res;
 }
 
-qint64 TrackFilter::trackfilter_parse_time_opt(const char* arg)
+qint64 TrackFilter::trackfilter_parse_time_opt(const QString& arg)
 {
   qint64 result = 0;
 
@@ -120,7 +119,7 @@ qint64 TrackFilter::trackfilter_parse_time_opt(const char* arg)
       qDebug() << MYNAME "-time option: shift =" << result / 1000.0 << "seconds";
     }
   } else {
-    fatal(MYNAME "-time: invalid value in move option \"%s\"!\n", arg);
+    fatal(MYNAME "-time: invalid value in move option \"%s\"!\n", qPrintable(arg));
   }
 
   return result;
@@ -141,23 +140,23 @@ fix_type TrackFilter::trackfilter_parse_fix(int* nsats)
   if (!opt_fix) {
     return fix_unknown;
   }
-  if (!case_ignore_strcmp(opt_fix.get(), "pps")) {
+  if (!case_ignore_strcmp(opt_fix, "pps")) {
     *nsats = 4;
     return fix_pps;
   }
-  if (!case_ignore_strcmp(opt_fix.get(), "dgps")) {
+  if (!case_ignore_strcmp(opt_fix, "dgps")) {
     *nsats = 4;
     return fix_dgps;
   }
-  if (!case_ignore_strcmp(opt_fix.get(), "3d")) {
+  if (!case_ignore_strcmp(opt_fix, "3d")) {
     *nsats = 4;
     return fix_3d;
   }
-  if (!case_ignore_strcmp(opt_fix.get(), "2d")) {
+  if (!case_ignore_strcmp(opt_fix, "2d")) {
     *nsats = 3;
     return fix_2d;
   }
-  if (!case_ignore_strcmp(opt_fix.get(), "none")) {
+  if (!case_ignore_strcmp(opt_fix, "none")) {
     *nsats = 0;
     return fix_none;
   }
@@ -191,8 +190,8 @@ void TrackFilter::trackfilter_fill_track_list_cb(const route_head* track)   /* ca
     return;
   }
 
-  if (opt_name != nullptr) {
-    QRegularExpression regex(QRegularExpression::wildcardToRegularExpression(opt_name.get()),
+  if (opt_name) {
+    QRegularExpression regex(QRegularExpression::wildcardToRegularExpression(opt_name),
                              QRegularExpression::CaseInsensitiveOption);
     if (!regex.isValid()) {
       fatal(FatalMsg() << "track: name option is an invalid expression.");
@@ -212,7 +211,7 @@ void TrackFilter::trackfilter_fill_track_list_cb(const route_head* track)   /* ca
     }
 
     if (need_time && (prev != nullptr) && (prev->GetCreationTime() > wpt->GetCreationTime())) {
-      if (opt_merge == nullptr) {
+      if (!opt_merge) {
         QString t1 = prev->CreationTimeXML();
         QString t2 = wpt->CreationTimeXML();
         fatal(MYNAME "-init: Track points badly ordered (timestamp %s > %s)!\n", qPrintable(t1), qPrintable(t2));
@@ -246,14 +245,14 @@ void TrackFilter::trackfilter_split_init_rte_name(route_head* track, const gpsba
     datetimestring = dt.toUTC().toString(u"yyyyMMdd");
   }
 
-  if ((opt_title != nullptr) && (strlen(opt_title) > 0)) {
-    if (strchr(opt_title, '%') != nullptr) {
+  if (!opt_title.isEmpty()) {
+    if (opt_title.get().contains('%')) {
       // Uggh.  strftime format exposed to user.
 
       time_t time = dt.toTime_t();
       std::tm tm = *gmtime(&time);
       char buff[128];
-      strftime(buff, sizeof(buff), opt_title, &tm);
+      strftime(buff, sizeof(buff), opt_title.get().toUtf8(), &tm);
       track->rte_name = buff;
     } else {
       track->rte_name = QStringLiteral("%1-%2").arg(opt_title.get(), datetimestring);
@@ -267,7 +266,7 @@ void TrackFilter::trackfilter_split_init_rte_name(route_head* track, const gpsba
 
 void TrackFilter::trackfilter_pack_init_rte_name(route_head* track, const gpsbabel::DateTime& default_time)
 {
-  if (strchr(opt_title, '%') != nullptr) {
+  if (opt_title.get().contains('%')) {
     // Uggh.  strftime format exposed to user.
 
     gpsbabel::DateTime dt;
@@ -280,7 +279,7 @@ void TrackFilter::trackfilter_pack_init_rte_name(route_head* track, const gpsbab
     time_t t = dt.toTime_t();
     std::tm tm = *gmtime(&t);
     char buff[128];
-    strftime(buff, sizeof(buff), opt_title, &tm);
+    strftime(buff, sizeof(buff), opt_title.get().toUtf8(), &tm);
     track->rte_name = buff;
   } else {
     track->rte_name = opt_title;
@@ -293,11 +292,11 @@ void TrackFilter::trackfilter_pack_init_rte_name(route_head* track, const gpsbab
 
 void TrackFilter::trackfilter_title()
 {
-  if (opt_title == nullptr) {
+  if (!opt_title) {
     return;
   }
 
-  if (strlen(opt_title) == 0) {
+  if (opt_title.isEmpty()) {
     fatal(MYNAME "-title: Missing your title!\n");
   }
   for (auto* track : std::as_const(track_list)) {
@@ -435,11 +434,11 @@ void TrackFilter::trackfilter_split()
 
     /* check additional options */
 
-    opt_interval = (opt_split && (strlen(opt_split) > 0) && (0 != strcmp(opt_split, TRACKFILTER_SPLIT_OPTION)));
+    opt_interval = (!opt_split.isEmpty() && (opt_split.get() != TRACKFILTER_SPLIT_OPTION));
     if (opt_interval != 0) {
       static const QRegularExpression re(R"(^([+-]?(?:\d+(?:\.\d*)?|\.\d+))([dhms])$)", QRegularExpression::CaseInsensitiveOption);
       assert(re.isValid());
-      QRegularExpressionMatch match = re.match(opt_split.get());
+      QRegularExpressionMatch match = re.match(opt_split);
       if (match.hasMatch()) {
         bool ok;
         interval = match.captured(1).toDouble(&ok);
@@ -467,15 +466,15 @@ void TrackFilter::trackfilter_split()
           printf(MYNAME ": interval %f seconds\n", interval);
         }
       } else {
-        fatal(MYNAME ": invalid timer interval specified \"%s\", must be a positive number, followed by 'd' for days, 'h' for hours, 'm' for minutes or 's' for seconds.\n", qPrintable(opt_split.get()));
+        fatal(MYNAME ": invalid timer interval specified \"%s\", must be a positive number, followed by 'd' for days, 'h' for hours, 'm' for minutes or 's' for seconds.\n", qPrintable(opt_split));
       }
     }
 
-    opt_distance = (opt_sdistance && (strlen(opt_sdistance) > 0) && (0 != strcmp(opt_sdistance, TRACKFILTER_SDIST_OPTION)));
+    opt_distance = (!opt_sdistance.isEmpty() && (opt_sdistance.get() != TRACKFILTER_SDIST_OPTION));
     if (opt_distance != 0) {
       static const QRegularExpression re(R"(^([+-]?(?:\d+(?:\.\d*)?|\.\d+))([km])$)", QRegularExpression::CaseInsensitiveOption);
       assert(re.isValid());
-      QRegularExpressionMatch match = re.match(opt_sdistance.get());
+      QRegularExpressionMatch match = re.match(opt_sdistance);
       if (match.hasMatch()) {
         bool ok;
         distance = match.captured(1).toDouble(&ok);
@@ -665,13 +664,12 @@ void TrackFilter::trackfilter_synth()
 * option: "start" / "stop"
 *******************************************************************************/
 
-QDateTime TrackFilter::trackfilter_range_check(const char* timestr)
+QDateTime TrackFilter::trackfilter_range_check(const QString& timestr)
 {
   QDateTime result;
 
-  QString start(timestr);
   QString fmtstart("00000101000000.000");
-  fmtstart.replace(0, start.size(), start);
+  fmtstart.replace(0, timestr.size(), timestr);
 
   static const QRegularExpression re(R"(^\d{14}\.\d{3}$)");
   assert(re.isValid());
@@ -685,14 +683,14 @@ QDateTime TrackFilter::trackfilter_range_check(const char* timestr)
     result.setTimeSpec(Qt::UTC);
 #endif
     if (!result.isValid()) {
-      fatal(MYNAME "-range-check: Invalid timestamp \"%s\"!\n", timestr);
+      fatal(MYNAME "-range-check: Invalid timestamp \"%s\"!\n", qPrintable(timestr));
     }
 
     if constexpr(TRACKF_DBG) {
       qDebug() << MYNAME "-range-check: " << result;
     }
   } else {
-    fatal(MYNAME "-range-check: Invalid value for option \"%s\"!\n", timestr);
+    fatal(MYNAME "-range-check: Invalid value for option \"%s\"!\n", qPrintable(timestr));
   }
 
   return result;
@@ -703,11 +701,11 @@ void TrackFilter::trackfilter_range()
   QDateTime start; // constructed such that isValid() is false, unlike gpsbabel::DateTime!
   QDateTime stop;  // constructed such that isValid() is false, unlike gpsbabel::DateTime!
 
-  if (opt_start != nullptr) {
+  if (opt_start) {
     start = trackfilter_range_check(opt_start);
   }
 
-  if (opt_stop != nullptr) {
+  if (opt_stop) {
     stop = trackfilter_range_check(opt_stop);
   }
 
@@ -827,7 +825,7 @@ void TrackFilter::trackfilter_trk2seg()
 * option: "faketime"
 *******************************************************************************/
 
-TrackFilter::faketime_t TrackFilter::trackfilter_faketime_check(const char* timestr)
+TrackFilter::faketime_t TrackFilter::trackfilter_faketime_check(const QString& timestr)
 {
   faketime_t result;
 
@@ -864,7 +862,7 @@ TrackFilter::faketime_t TrackFilter::trackfilter_faketime_check(const char* time
       qDebug() << MYNAME "-faketime option: force =" << result.force << ", timestamp =" << result.start << ", step =" << result.step << "milliseconds";
     }
   } else {
-    fatal(MYNAME "-faketime-check: Invalid value for faketime option \"%s\"!\n", timestr);
+    fatal(MYNAME "-faketime-check: Invalid value for faketime option \"%s\"!\n", qPrintable(timestr));
   }
 
   return result;
@@ -872,7 +870,7 @@ TrackFilter::faketime_t TrackFilter::trackfilter_faketime_check(const char* time
 
 void TrackFilter::trackfilter_faketime()
 {
-  assert(opt_faketime != nullptr);
+  assert(opt_faketime);
   faketime_t faketime = trackfilter_faketime_check(opt_faketime);
 
   for (auto* track : std::as_const(track_list)) {
@@ -961,7 +959,7 @@ void TrackFilter::init()
                 (trackfilter_opt_count() == 0) /* do pack by default */
               );
   /* in case of a formatted title we also need valid timestamps */
-  if ((opt_title != nullptr) && (strchr(opt_title, '%') != nullptr)) {
+  if (opt_title && opt_title.get().contains('%')) {
     need_time = true;
   }
 
@@ -1003,13 +1001,13 @@ void TrackFilter::process()
     opts = -1;  /* flag for do "pack" by default */
   }
 
-  if (opt_name != nullptr) {
+  if (opt_name) {
     if (--opts == 0) {
       return;
     }
   }
 
-  if (opt_move != nullptr) {           /* Correct timestamps before any other op */
+  if (opt_move) {              /* Correct timestamps before any other op */
     trackfilter_move();
     if (--opts == 0) {
       return;
@@ -1032,7 +1030,7 @@ void TrackFilter::process()
     }
   }
 
-  if ((opt_faketime != nullptr)) {
+  if (opt_faketime) {
     opts--;
 
     trackfilter_faketime();
@@ -1050,11 +1048,11 @@ void TrackFilter::process()
     }
   }
 
-  if ((opt_stop != nullptr) || (opt_start != nullptr)) {
-    if (opt_start != nullptr) {
+  if (opt_stop || opt_start) {
+    if (opt_start) {
       opts--;
     }
-    if (opt_stop != nullptr) {
+    if (opt_stop) {
       opts--;
     }
 
@@ -1093,7 +1091,7 @@ void TrackFilter::process()
     }
   }
 
-  if (opt_title != nullptr) {
+  if (opt_title) {
     if (--opts == 0) {
       trackfilter_title();
       return;
@@ -1105,28 +1103,27 @@ void TrackFilter::process()
   if (opt_pack || (opts == -1)) {      /* call our default option */
     trackfilter_pack();
     something_done = true;
-  } else if (opt_merge != nullptr) {
+  } else if (opt_merge) {
     trackfilter_merge();
     something_done = true;
   }
 
   if (something_done && (--opts <= 0)) {
-    if (opt_title != nullptr) {
+    if (opt_title) {
       trackfilter_title();
     }
     return;
   }
 
-  if ((opt_split != nullptr) || (opt_sdistance != nullptr)) {
+  if (opt_split || opt_sdistance) {
     trackfilter_split();
     // track_list may? now be invalid!
   }
 
   // Performed last as previous options may have created "small" tracks.
-  if (opt_minpoints != nullptr) {
-    bool ok;
-    minimum_points = QString(opt_minpoints).toInt(&ok);
-    if (!ok || minimum_points <= 0) {
+  if (opt_minpoints) {
+    minimum_points = opt_minpoints.get_result();
+    if (minimum_points <= 0) {
       fatal(MYNAME "-minimum_points: option value must be a positive integer!\n");
     }
     track_disp_all(trackfilter_minpoint_list_cb_f, nullptr, nullptr);
index 70d8e5e28c6ab16c9553dbcdd1a005ba6c2d45f7..c5b31fabbca8966f2ed2c143d4be8ab79331222c 100644 (file)
@@ -30,7 +30,7 @@
 
 #include "defs.h"               // for ARG_NOMINMAX, route_head (ptr only), ARG...
 #include "filter.h"             // for Filter
-#include "option.h"             // for OptionCString, OptionBool
+#include "option.h"             // for OptionString, OptionBool
 #include "src/core/datetime.h"  // for DateTime
 
 #if FILTERS_ENABLED || MINIMAL_FILTERS
@@ -81,7 +81,7 @@ private:
   /* Member Functions */
 
   int trackfilter_opt_count();
-  static qint64 trackfilter_parse_time_opt(const char* arg);
+  static qint64 trackfilter_parse_time_opt(const QString& arg);
   static bool trackfilter_init_sort_cb(const route_head* ha, const route_head* hb);
   static bool trackfilter_merge_sort_cb(const Waypoint* wa, const Waypoint* wb);
   fix_type trackfilter_parse_fix(int* nsats);
@@ -97,35 +97,35 @@ private:
   void trackfilter_split();
   void trackfilter_move();
   void trackfilter_synth();
-  static QDateTime trackfilter_range_check(const char* timestr);
+  static QDateTime trackfilter_range_check(const QString& timestr);
   void trackfilter_range();
   void trackfilter_seg2trk();
   void trackfilter_trk2seg();
-  static faketime_t trackfilter_faketime_check(const char* timestr);
+  static faketime_t trackfilter_faketime_check(const QString& timestr);
   void trackfilter_faketime();
   static bool trackfilter_points_are_same(const Waypoint* wpta, const Waypoint* wptb);
   static void trackfilter_segment_head(const route_head* rte);
 
   /* Data Members */
 
-  OptionCString opt_merge;
+  OptionString opt_merge;
   OptionBool opt_pack;
-  OptionCString opt_split;
-  OptionCString opt_sdistance;
-  OptionCString opt_move;
-  OptionCString opt_title;
-  OptionCString opt_start;
-  OptionCString opt_stop;
-  OptionCString opt_fix;
+  OptionString opt_split;
+  OptionString opt_sdistance;
+  OptionString opt_move;
+  OptionString opt_title;
+  OptionString opt_start;
+  OptionString opt_stop;
+  OptionString opt_fix;
   OptionBool opt_course;
   OptionBool opt_speed;
-  OptionCString opt_name;
+  OptionString opt_name;
   OptionBool opt_seg2trk;
   OptionBool opt_trk2seg;
   OptionBool opt_segment;
-  OptionCString opt_faketime;
+  OptionString opt_faketime;
   OptionBool opt_discard;
-  OptionCString opt_minpoints;
+  OptionInt opt_minpoints;
   int minimum_points{0};
 
   QVector<arglist_t> args = {
index ed90e0d49433b249117b8a8d64b9ba5da12a67d5..bb5c98e946f03a657d0c5baa8188e3958a7793e8 100644 (file)
@@ -138,65 +138,56 @@ void TransformFilter::process()
   use_src_name = opt_rpt_name;
 
   name_digits = 3;
-  if (rpt_name_digits && *rpt_name_digits) {
-    name_digits = xstrtoi(rpt_name_digits, nullptr, 10);
+  if (!rpt_name_digits.isEmpty()) {
+    name_digits = rpt_name_digits.get_result();
   }
 
-  if (opt_waypts != nullptr) {
+  if (opt_waypts) {
     current_target = 'W';
-    switch (toupper(*opt_waypts)) {
-    case 'R':
+    if (opt_waypts.get().startsWith('R', Qt::CaseInsensitive)) {
       transform_routes();
       if (delete_after) {
         route_flush_all_routes();
       }
-      break;
-    case 'T':
+    } else if (opt_waypts.get().startsWith('T', Qt::CaseInsensitive)) {
       transform_tracks();
       if (delete_after) {
         route_flush_all_tracks();
       }
-      break;
-    default:
-      fatal(MYNAME ": Invalid option value (%s)!\n", qPrintable(opt_waypts.get()));
+    } else {
+      fatal(MYNAME ": Invalid option value (%s)!\n", qPrintable(opt_waypts));
     }
   }
-  if (opt_routes != nullptr) {
+  if (opt_routes) {
     current_target = 'R';
-    switch (toupper(*opt_routes)) {
-    case 'W':
+    if (opt_routes.get().startsWith('W', Qt::CaseInsensitive)) {
       transform_waypoints();
       if (delete_after) {
         waypt_flush_all();
       }
-      break;
-    case 'T':
+    } else if (opt_routes.get().startsWith('T', Qt::CaseInsensitive)) {
       transform_tracks();
       if (delete_after) {
         route_flush_all_tracks();
       }
-      break;
-    default:
-      fatal(MYNAME ": Invalid option value (%s)!\n", qPrintable(opt_routes.get()));
+    } else {
+      fatal(MYNAME ": Invalid option value (%s)!\n", qPrintable(opt_routes));
     }
   }
-  if (opt_tracks != nullptr) {
+  if (opt_tracks) {
     current_target = 'T';
-    switch (toupper(*opt_tracks)) {
-    case 'W':
+    if (opt_tracks.get().startsWith('W', Qt::CaseInsensitive)) {
       transform_waypoints();
       if (delete_after) {
         waypt_flush_all();
       }
-      break;
-    case 'R':
+    } else if (opt_tracks.get().startsWith('R', Qt::CaseInsensitive)) {
       transform_routes();
       if (delete_after) {
         route_flush_all_routes();
       }
-      break;
-    default:
-      fatal(MYNAME ": Invalid option value (%s)!\n", qPrintable(opt_tracks.get()));
+    } else {
+      fatal(MYNAME ": Invalid option value (%s)!\n", qPrintable(opt_tracks));
     }
   }
 }
index e992ba29ed3a210e547556ec7f5979ddbd81053a..f3011c6de3560d47e6cd656f765db23759985ff2 100644 (file)
@@ -29,7 +29,7 @@
 
 #include "defs.h"    // for route_head (ptr only), ARG_NOMINMAX, ARGTY...
 #include "filter.h"  // for Filter
-#include "option.h"  // for OptionCString, OptionBool
+#include "option.h"  // for OptionString, OptionBool
 
 #if FILTERS_ENABLED
 
@@ -47,11 +47,11 @@ private:
   route_head* current_trk{};
   route_head* current_rte{};
 
-  OptionCString opt_routes;
-  OptionCString opt_tracks;
-  OptionCString opt_waypts;
+  OptionString opt_routes;
+  OptionString opt_tracks;
+  OptionString opt_waypts;
   OptionBool opt_delete;
-  OptionCString rpt_name_digits;
+  OptionInt rpt_name_digits;
   OptionBool opt_rpt_name;
   OptionBool opt_timeless;
   bool timeless{};
index 2b0a247dd3e2b75144d06370107e9d3aad23e864..bae0737dd8b8f49d60b8984349cbe4241fdcaf08 100644 (file)
--- a/unicsv.cc
+++ b/unicsv.cc
@@ -344,7 +344,7 @@ UnicsvFormat::unicsv_parse_status(const QString& str)
 QDateTime
 UnicsvFormat::unicsv_adjust_time(const QDate date, const QTime time, bool is_localtime) const
 {
-  return make_datetime(date, time, is_localtime, opt_utc != nullptr, utc_offset);
+  return make_datetime(date, time, is_localtime, opt_utc, utc_offset);
 }
 
 bool
@@ -465,13 +465,14 @@ UnicsvFormat::rd_init(const QString& fname)
   unicsv_detect = (!(global_opts.masked_objective & (WPTDATAMASK | TRKDATAMASK | RTEDATAMASK | POSNDATAMASK)));
 
   unicsv_track = unicsv_route = nullptr;
-  unicsv_datum_idx = gt_lookup_datum_index(opt_datum.get(), MYNAME);
+  unicsv_datum_idx = gt_lookup_datum_index(opt_datum, MYNAME);
 
   fin = new gpsbabel::TextStream;
-  fin->open(fname, QIODevice::ReadOnly, MYNAME, opt_codec);
+  fin->open(fname, QIODevice::ReadOnly, MYNAME, opt_codec.get().toUtf8());
   unicsv_lineno = 0;
   if (opt_fields) {
-    QString fields = QString(opt_fields).replace('+', ',');
+    QString fields = opt_fields;
+    fields.replace('+', ',');
     unicsv_fondle_header(fields);
   } else if (buff = fin->readLine(); !buff.isNull()) {
     ++unicsv_lineno;
@@ -480,7 +481,7 @@ UnicsvFormat::rd_init(const QString& fname)
     unicsv_fieldsep = nullptr;
   }
 
-  utc_offset = (opt_utc == nullptr)? 0 : xstrtoi(opt_utc, nullptr, 10) * SECONDS_PER_HOUR;
+  utc_offset = opt_utc? opt_utc.get_result() * SECONDS_PER_HOUR : 0;
 }
 
 void
@@ -1118,7 +1119,7 @@ UnicsvFormat::unicsv_print_date_time(const QDateTime& idt) const
     return;
   }
   QDateTime dt;
-  if (opt_utc != nullptr) {
+  if (opt_utc) {
     dt = idt.toOffsetFromUtc(utc_offset);
   } else {
     dt = idt.toLocalTime();
@@ -1509,7 +1510,7 @@ UnicsvFormat::unicsv_waypt_disp_cb(const Waypoint* wpt)
   if (unicsv_outp_flags[fld_date]) {
     if (wpt->creation_time.toTime_t() >= 2 * SECONDS_PER_DAY) {
       QDateTime dt;
-      if (opt_utc != nullptr) {
+      if (opt_utc) {
         dt = wpt->GetCreationTime().toOffsetFromUtc(utc_offset);
       } else {
         dt = wpt->GetCreationTime().toLocalTime();
@@ -1523,7 +1524,7 @@ UnicsvFormat::unicsv_waypt_disp_cb(const Waypoint* wpt)
   if (unicsv_outp_flags[fld_time]) {
     if (wpt->creation_time.isValid()) {
       QDateTime dt;
-      if (opt_utc != nullptr) {
+      if (opt_utc) {
         dt = wpt->GetCreationTime().toOffsetFromUtc(utc_offset);
       } else {
         dt = wpt->GetCreationTime().toLocalTime();
@@ -1679,7 +1680,7 @@ UnicsvFormat::wr_init(const QString& fname)
           ": option 'fields' is not supported on output");
   }
   fout = new gpsbabel::TextStream;
-  fout->open(fname, QIODevice::WriteOnly, MYNAME, opt_codec);
+  fout->open(fname, QIODevice::WriteOnly, MYNAME, opt_codec.get().toUtf8());
   fout->setRealNumberNotation(QTextStream::FixedNotation);
 
   unicsv_outp_flags.reset();
@@ -1688,16 +1689,16 @@ UnicsvFormat::wr_init(const QString& fname)
   unicsv_fieldsep = kUnicsvFieldSep;
   unicsv_waypt_ct = 0;
 
-  if (opt_grid != nullptr) {
-    int i;
+  if (!opt_grid.isEmpty()) {
+    bool ok;
 
-    if (sscanf(opt_grid, "%d", &i)) {
+    if (int i = opt_grid.toInt(&ok); ok) {
       unicsv_grid_idx = (grid_type) i;
       if ((unicsv_grid_idx < GRID_INDEX_MIN) || (unicsv_grid_idx > GRID_INDEX_MAX))
         fatal(MYNAME ": Grid index out of range (%d..%d)!\n",
               (int)GRID_INDEX_MIN, (int)GRID_INDEX_MAX);
     } else {
-      unicsv_grid_idx = gt_lookup_grid_type(opt_grid.get(), MYNAME);
+      unicsv_grid_idx = gt_lookup_grid_type(opt_grid, MYNAME);
     }
   }
 
@@ -1711,11 +1712,11 @@ UnicsvFormat::wr_init(const QString& fname)
   {
     unicsv_datum_idx = kDatumWGS84;  /* internal, becomes CH1903 */
   } else {
-    unicsv_datum_idx = gt_lookup_datum_index(opt_datum.get(), MYNAME);
+    unicsv_datum_idx = gt_lookup_datum_index(opt_datum, MYNAME);
   }
 
-  llprec = xstrtoi(opt_prec, nullptr, 10);
-  utc_offset = (opt_utc == nullptr)? 0 : xstrtoi(opt_utc, nullptr, 10) * SECONDS_PER_HOUR;
+  llprec = opt_prec.get_result();
+  utc_offset = opt_utc? opt_utc.get_result() * SECONDS_PER_HOUR : 0;
 }
 
 void
index 2371fe373f09ebce5d01d225bdef8ef244b14765..4ecc998f46b1e758f9288b95b0d90089d958128f 100644 (file)
--- a/unicsv.h
+++ b/unicsv.h
@@ -34,7 +34,7 @@
 #include "defs.h"
 #include "format.h"               // for Format
 #include "geocache.h"             // for Geocache, Geocache::status_t
-#include "option.h"               // for OptionCString, OptionBool
+#include "option.h"               // for OptionString, OptionBool
 #include "src/core/textstream.h"  // for TextStream
 
 
@@ -200,14 +200,14 @@ private:
   std::bitset<fld_terminator> unicsv_outp_flags;
   grid_type unicsv_grid_idx{grid_unknown};
   int unicsv_datum_idx{};
-  OptionCString opt_datum;
-  OptionCString opt_grid;
-  OptionCString opt_utc;
+  OptionString opt_datum;
+  OptionString opt_grid;
+  OptionInt opt_utc;
   OptionBool opt_filename;
   OptionBool opt_format;
-  OptionCString opt_prec;
-  OptionCString opt_fields;
-  OptionCString opt_codec;
+  OptionInt opt_prec;
+  OptionString opt_fields;
+  OptionString opt_codec;
   int unicsv_waypt_ct{};
   char unicsv_detect{};
   int llprec{};
diff --git a/vecs.cc b/vecs.cc
index d21a988dc1bfc95859976b9c03072ce8bea6adec..54d26690bbe67bad540f0533972b02aa28a8dfee 100644 (file)
--- a/vecs.cc
+++ b/vecs.cc
@@ -525,57 +525,60 @@ void Vecs::init_vecs()
   style_list = create_style_vec();
 }
 
-bool Vecs::is_integer(const QString& val)
+int Vecs::integer_base(uint32_t argtype)
 {
-#if 1
-  /* FIXME: Using scanf to validate input is not recommened.
-   * Users may have taken advantage of this flexibilty
-   * when interpreting ARGTYPE_INT.
-   * INT05-C. Do not use input functions to convert character
-   * data if they cannot handle all possible inputs
-   */
-  // note sscanf doesn't do range checking
-  // note some users allow hex input.
-  // note some users may interpret trailing data after
-  // conversion, typically to denote a unit.
-  int test;
-  return 1 == sscanf(CSTR(val), "%d", &test);
-#else
-  try {
-    (void) std::stoi(val.toStdString(), nullptr, 10);
-  } catch (const std::invalid_argument&) {
-    return false;
-  } catch (const std::out_of_range&) {
-    return false;
+  int base;
+  switch (argtype & ARGTYPE_BASEMASK) {
+  case ARGTYPE_BASE_AUTO:
+    base = 0;
+    break;
+  case ARGTYPE_BASE_16:
+    base = 16;
+    break;
+  case ARGTYPE_BASE_10:
+  default:
+    base = 10;
   }
-  return true;
-#endif
+  return base;
+
 }
 
-bool Vecs::is_float(const QString& val)
+bool Vecs::trailing_data_allowed(uint32_t argtype)
 {
-#if 1
-  /* FIXME: Using scanf to validate input is not recommened.
-   * Users may have taken advantage of this flexibilty
-   * when interpreting ARGTYPE_FLOAT.
-   * INT05-C. Do not use input functions to convert character
-   * data if they cannot handle all possible inputs
-   */
-  // note sscanf doesn't do range checking
-  // note some users may interpret trailing data after
-  // conversion, typically to denote a unit.
-  double test;
-  return 1 == sscanf(CSTR(val), "%lf", &test);
-#else
-  try {
-    (void) std::stod(val.toStdString(), nullptr);
-  } catch (const std::invalid_argument&) {
-    return false;
-  } catch (const std::out_of_range&) {
-    return false;
-  }
-  return true;
-#endif
+  return (argtype & ARGTYPE_ALLOW_TRAILING_DATA) == ARGTYPE_ALLOW_TRAILING_DATA;
+}
+
+bool Vecs::is_integer(const QString& val, const QString& id, uint32_t argtype)
+{
+  bool ok;
+  int base = integer_base(argtype);
+  QString end;
+  QString* endp = trailing_data_allowed(argtype) ? &end : nullptr;
+  (void) parse_integer(val, id, &ok, endp, base);
+  return ok;
+}
+
+int Vecs::convert_integer(const QString& val, const QString& id, QString* end, int base)
+{
+  // Fatal on conversion error
+  constexpr bool* dieonerror = nullptr;
+  return parse_integer(val, id, dieonerror, end, base);
+}
+
+bool Vecs::is_float(const QString& val, const QString& id, uint32_t argtype)
+{
+  bool ok;
+  QString end;
+  QString* endp = trailing_data_allowed(argtype) ? &end : nullptr;
+  (void) parse_double(val, id, &ok, endp);
+  return ok;
+}
+
+double Vecs::convert_float(const QString& val, const QString& id, QString* end)
+{
+  // Fatal on conversion error
+  constexpr bool* dieonerror = nullptr;
+  return parse_double(val, id, dieonerror, end);
 }
 
 bool Vecs::is_bool(const QString& val)
@@ -616,11 +619,14 @@ void Vecs::exit_vecs()
 
 void Vecs::assign_option(const QString& module, arglist_t& arg, const QString& val)
 {
+  QString id = QStringLiteral("%1(%2)").arg(module, arg.argstring);
+
   if (arg.argval == nullptr) {
-    fatal("%s: No local variable defined for option \"%s\"!\n", qPrintable(module), qPrintable(arg.argstring));
+    fatal("%s: Program error - No local variable defined for option.\n", qPrintable(id));
   }
 
   arg.argval->reset();
+  arg.argval->set_id(id);
 
   if (val.isNull()) {
     return;
@@ -628,26 +634,30 @@ void Vecs::assign_option(const QString& module, arglist_t& arg, const QString& v
 
   QString rval(val);
 
-  switch (arg.argtype & ARGTYPE_TYPEMASK) {
-  case ARGTYPE_INT:
+  QString end;
+  QString* endp = trailing_data_allowed(arg.argtype)? &end: nullptr;
+
+  if (auto* int_option = dynamic_cast<OptionInt*>(arg.argval); int_option != nullptr) {
+    int result;
     if (val.isEmpty()) {
       rval = '0';
+      result = 0;
     } else {
-      if (!is_integer(val)) {
-        fatal("%s: Invalid parameter value \"%s\" for option %s!\n", qPrintable(module), qPrintable(val), qPrintable(arg.argstring));
-      }
+      // will fatal on conversion error
+      result = convert_integer(val, id, endp, integer_base(arg.argtype));
     }
-    break;
-  case ARGTYPE_FLOAT:
+    int_option->set_result(result, end);
+  } else if (auto* double_option = dynamic_cast<OptionDouble*>(arg.argval); double_option != nullptr) {
+    double result;
     if (val.isEmpty()) {
       rval = '0';
+      result = 0.0;
     } else {
-      if (!is_float(val)) {
-        fatal("%s: Invalid parameter value \"%s\" for option %s!\n", qPrintable(module), qPrintable(val), qPrintable(arg.argstring));
-      }
+      // will fatal on conversion error
+      result = convert_float(val, id, endp);
     }
-    break;
-  case ARGTYPE_BOOL:
+    double_option->set_result(result, end);
+  } else if (auto* bool_option = dynamic_cast<OptionBool*>(arg.argval); bool_option != nullptr) {
     if (val.isEmpty()) {
       rval = '1';
     } else {
@@ -664,12 +674,11 @@ void Vecs::assign_option(const QString& module, arglist_t& arg, const QString& v
             rval = '1';
           }
         } else {
-          warning("%s :Invalid logical value \"%s\" for option %s!\n", qPrintable(module), qPrintable(val), qPrintable(arg.argstring));
+          warning("%s: Invalid logical value \"%s\".\n", qPrintable(id), qPrintable(val));
           rval = '0';
         }
       }
     }
-    break;
   }
 
   arg.argval->set(rval);
@@ -1124,37 +1133,69 @@ bool Vecs::validate_args(const QString& name, const QVector<arglist_t>* args)
     }
 #endif
     for (const auto& arg : *args) {
+      QString id = QStringLiteral("%1(%2)").arg(name, arg.argstring);
       if (arg.argval == nullptr) {
         Warning() << name << "option" << arg.argstring << "does not point to an Option instance.";
         ok = false;
       }
-      if ((arg.argtype & ARGTYPE_TYPEMASK) == ARGTYPE_INT) {
-        if (!arg.defaultvalue.isNull() && !is_integer(arg.defaultvalue)) {
+      if (const auto* int_option = dynamic_cast<const OptionInt*>(arg.argval); int_option != nullptr) {
+        if (trailing_data_allowed(arg.argtype)) {
+          // GUI QIntValidator will reject input with trailing data.
+          if ((arg.argtype & ARGTYPE_TYPEMASK) != ARGTYPE_STRING) {
+            Warning() << name << "OptionInt with trailing data" << arg.argstring << "is not of ARGTYPE_STRING.";
+            ok = false;
+          }
+        } else {
+          if ((arg.argtype & ARGTYPE_TYPEMASK) != ARGTYPE_INT) {
+            Warning() << name << "OptionInt option without trailing data" << arg.argstring << "is not of ARGTYPE_INT.";
+            ok = false;
+          }
+        }
+
+        if (!arg.defaultvalue.isNull() && !is_integer(arg.defaultvalue, id, arg.argtype)) {
           Warning() << name << "Int option" << arg.argstring << "default value" << arg.defaultvalue << "is not an integer.";
           ok = false;
         }
-        if (!arg.minvalue.isNull() && !is_integer(arg.minvalue)) {
+        if (!arg.minvalue.isNull() && !is_integer(arg.minvalue, id, arg.argtype)) {
           Warning() << name << "Int option" << arg.argstring << "minimum value" << arg.minvalue << "is not an integer.";
           ok = false;
         }
-        if (!arg.maxvalue.isNull() && !is_integer(arg.maxvalue)) {
+        if (!arg.maxvalue.isNull() && !is_integer(arg.maxvalue, id, arg.argtype)) {
           Warning() << name << "Int option" << arg.argstring << "maximum value" << arg.maxvalue << "is not an integer.";
           ok = false;
         }
-      } else if ((arg.argtype & ARGTYPE_TYPEMASK) == ARGTYPE_FLOAT) {
-        if (!arg.defaultvalue.isNull() && !is_float(arg.defaultvalue)) {
+      } else if (const auto* double_option = dynamic_cast<const OptionDouble*>(arg.argval); double_option != nullptr) {
+        if (trailing_data_allowed(arg.argtype)) {
+          // GUI QDoubleValidator will reject input with trailing data.
+          if ((arg.argtype & ARGTYPE_TYPEMASK) != ARGTYPE_STRING) {
+            Warning() << name << "OptionDouble with trailing data" << arg.argstring << "is not of ARGTYPE_STRING.";
+            ok = false;
+          }
+        } else {
+          if ((arg.argtype & ARGTYPE_TYPEMASK) != ARGTYPE_FLOAT) {
+            Warning() << name << "OptionDouble without trailing data" << arg.argstring << "is not of ARGTYPE_FLOAT.";
+            ok = false;
+          }
+        }
+
+        if (!arg.defaultvalue.isNull() && !is_float(arg.defaultvalue, id, arg.argtype)) {
           Warning() << name << "Float option" << arg.argstring << "default value" << arg.defaultvalue << "is not an float.";
           ok = false;
         }
-        if (!arg.minvalue.isNull() && !is_float(arg.minvalue)) {
+        if (!arg.minvalue.isNull() && !is_float(arg.minvalue, id, arg.argtype)) {
           Warning() << name << "Float option" << arg.argstring << "minimum value" << arg.minvalue << "is not an float.";
           ok = false;
         }
-        if (!arg.maxvalue.isNull() && !is_float(arg.maxvalue)) {
+        if (!arg.maxvalue.isNull() && !is_float(arg.maxvalue, id, arg.argtype)) {
           Warning() << name << "Float option" << arg.argstring << "maximum value" << arg.maxvalue << "is not an float.";
           ok = false;
         }
-      } else if ((arg.argtype & ARGTYPE_TYPEMASK) == ARGTYPE_BOOL) {
+      } else if (const auto* bool_option = dynamic_cast<const OptionBool*>(arg.argval); bool_option != nullptr) {
+        if ((arg.argtype & ARGTYPE_TYPEMASK) != ARGTYPE_BOOL) {
+          Warning() << name << "OptionBool" << arg.argstring << "is not of ARGTYPE_BOOL.";
+          ok = false;
+        }
+
         if (!arg.defaultvalue.isNull() && !is_bool(arg.defaultvalue)) {
           Warning() << name << "Bool option" << arg.argstring << "default value" << arg.defaultvalue << "is not an bool.";
           ok = false;
@@ -1167,16 +1208,32 @@ bool Vecs::validate_args(const QString& name, const QVector<arglist_t>* args)
           Warning() << name << "Bool option" << arg.argstring << "maximum value" << arg.maxvalue << "is not an bool.";
           ok = false;
         }
-        if (const auto* opt = dynamic_cast<const OptionBool*>(arg.argval); opt == nullptr) {
-          Warning() << name << "Bool option" << arg.argstring << "argval is not of class OptionBool";
+      } else if (const auto* str_option = dynamic_cast<const OptionString*>(arg.argval); str_option != nullptr) {
+        if (((arg.argtype & ARGTYPE_TYPEMASK) != ARGTYPE_STRING) &&
+            ((arg.argtype & ARGTYPE_TYPEMASK) != ARGTYPE_FILE) &&
+            ((arg.argtype & ARGTYPE_TYPEMASK) != ARGTYPE_OUTFILE) &&
+            ((arg.argtype & ARGTYPE_TYPEMASK) != ARGTYPE_UNKNOWN)) {
+          Warning() << name << "OptionString" << arg.argstring << "is not of ARGTYPE STRING, FILE, OUTFILE or UNKNOWN.";
           ok = false;
         }
+      } else {
+        Warning() << name << "Unexpected Option type" << arg.argstring << ".";
+        ok = false;
       }
-      if ((arg.argtype & ARGTYPE_TYPEMASK) != ARGTYPE_BOOL) {
-        if (const auto* opt = dynamic_cast<const OptionBool*>(arg.argval); opt != nullptr) {
-          Warning() << name << "non Bool option" << arg.argstring << "argval is of class OptionBool";
-          ok = false;
-        }
+
+      switch (arg.argtype & ARGTYPE_TYPEMASK) {
+      case ARGTYPE_INT:
+      case ARGTYPE_FLOAT:
+      case ARGTYPE_BOOL:
+      case ARGTYPE_STRING:
+      case ARGTYPE_FILE:
+      case ARGTYPE_OUTFILE:
+        break;
+      case ARGTYPE_UNKNOWN:
+      default:
+        Warning() << name << "Unknown ARGTYPE for << arg.argstring.";
+        ok = false;
+        break;
       }
     }
   }
diff --git a/vecs.h b/vecs.h
index f1be8f1a7ec58f80cce25054f5c14cc6b649c79b..118d09499b6a4cf6fbfb4899c88db0f0a3f57cc6 100644 (file)
--- a/vecs.h
+++ b/vecs.h
@@ -143,8 +143,12 @@ private:
 
   /* Member Functions */
 
-  static bool is_integer(const QString& val);
-  static bool is_float(const QString& val);
+  static int integer_base(uint32_t argtype);
+  static bool trailing_data_allowed(uint32_t argtype);
+  static bool is_integer(const QString& val, const QString& id, uint32_t argtype);
+  static int convert_integer(const QString& val, const QString& id, QString* end, int base);
+  static bool is_float(const QString& val, const QString& id, uint32_t argtype);
+  static double convert_float(const QString& val, const QString& id, QString* end);
   static bool is_bool(const QString& val);
   static QVector<style_vec_t> create_style_vec();
   QVector<vecinfo_t> sort_and_unify_vecs() const;
diff --git a/xcsv.cc b/xcsv.cc
index 74f94c65e15ce0782fca027721e25f38d5c486f5..7b3008c415e6e5b94acc936fb605f47b5e6b8982 100644 (file)
--- a/xcsv.cc
+++ b/xcsv.cc
@@ -253,7 +253,7 @@ XcsvFormat::yyyymmdd_to_time(const QString& s)
 QDateTime
 XcsvFormat::xcsv_adjust_time(const QDate date, const QTime time, bool is_localtime) const
 {
-  return make_datetime(date, time, is_localtime, opt_utc != nullptr, utc_offset);
+  return make_datetime(date, time, is_localtime, opt_utc, utc_offset);
 }
 
 /*
@@ -1882,7 +1882,7 @@ XcsvFormat::rd_init(const QString& fname)
       fatal(MYNAME ": XCSV input style not declared.  Use ... -i xcsv,style=path/to/file.style\n");
     }
 
-    xcsv_style = new XcsvStyle(XcsvStyle::xcsv_read_style(styleopt.get()));
+    xcsv_style = new XcsvStyle(XcsvStyle::xcsv_read_style(styleopt));
   }
 
   if ((xcsv_style->datatype == 0) || (xcsv_style->datatype == wptdata)) {
@@ -1900,7 +1900,7 @@ XcsvFormat::rd_init(const QString& fname)
   xcsv_file->fname = fname;
 
   QString datum_name;
-  if (opt_datum != nullptr) {
+  if (opt_datum) {
     datum_name = opt_datum;
   } else if (!xcsv_style->gps_datum_name.isEmpty()) {
     datum_name = xcsv_style->gps_datum_name;
@@ -1912,7 +1912,7 @@ XcsvFormat::rd_init(const QString& fname)
     fatal(MYNAME ": datum \"%s\" is not supported.", qPrintable(datum_name));
   }
 
-  utc_offset = (opt_utc == nullptr)? 0 : xstrtoi(opt_utc, nullptr, 10) * SECONDS_PER_HOUR;
+  utc_offset = opt_utc? opt_utc.get_result() * SECONDS_PER_HOUR : 0;
 }
 
 void
@@ -1940,7 +1940,7 @@ XcsvFormat::wr_init(const QString& fname)
       fatal(MYNAME ": XCSV output style not declared.  Use ... -o xcsv,style=path/to/file.style\n");
     }
 
-    xcsv_style = new XcsvStyle(XcsvStyle::xcsv_read_style(styleopt.get()));
+    xcsv_style = new XcsvStyle(XcsvStyle::xcsv_read_style(styleopt));
   }
 
   xcsv_file = new XcsvFile;
@@ -1962,7 +1962,7 @@ XcsvFormat::wr_init(const QString& fname)
   if (global_opts.synthesize_shortnames) {
 
     if (snlenopt) {
-      xcsv_file->mkshort_handle.set_length(xstrtoi(snlenopt, nullptr, 10));
+      xcsv_file->mkshort_handle.set_length(snlenopt.get_result());
     }
 
     if (snwhiteopt.has_value()) {
@@ -1982,7 +1982,7 @@ XcsvFormat::wr_init(const QString& fname)
   }
 
   QString datum_name;
-  if (opt_datum != nullptr) {
+  if (opt_datum) {
     datum_name = opt_datum;
   } else if (!xcsv_style->gps_datum_name.isEmpty()) {
     datum_name = xcsv_style->gps_datum_name;
diff --git a/xcsv.h b/xcsv.h
index d76ce8e1fc7fbf7a9fad38bb9bb89dc9a534048b..47fb07e29fd1549eb6f1401648c7d19a2394f0c4 100644 (file)
--- a/xcsv.h
+++ b/xcsv.h
@@ -40,7 +40,7 @@
 #include "format.h"               // for Format
 #include "garmin_fs.h"            // for garmin_fs_t
 #include "mkshort.h"              // for MakeShort
-#include "option.h"               // for OptionCString, OptionBool
+#include "option.h"               // for OptionString, OptionBool
 #include "src/core/datetime.h"    // for DateTime
 #include "src/core/textstream.h"  // for TextStream
 
@@ -371,15 +371,15 @@ private:
   const route_head* csv_track = nullptr;
   const route_head* csv_route = nullptr;
 
-  OptionCString styleopt;
-  OptionCString snlenopt;
+  OptionString styleopt;
+  OptionInt snlenopt;
   OptionBool snwhiteopt;
   OptionBool snupperopt;
   OptionBool snuniqueopt;
   OptionBool prefer_shortnames;
-  OptionCString xcsv_urlbase;
-  OptionCString opt_datum;
-  OptionCString opt_utc;
+  OptionString xcsv_urlbase;
+  OptionString opt_datum;
+  OptionInt opt_utc;
   int utc_offset{};
 
   QString intstylefile;